From 9f518de27b9be0e374bc5234f9951306a0786447 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 22 Dec 2019 23:43:36 +0200 Subject: [PATCH 01/67] Build system is now working for macOS. --- bin/4ed_build.cpp | 1226 ++++----- bin/build.sh | 5 +- custom/4coder_base_types.cpp | 14 +- custom/4coder_code_index.cpp | 2346 ++++++++--------- custom/4coder_draw.cpp | 1686 ++++++------ custom/4coder_malloc_allocator.cpp | 5 +- custom/4coder_types.h | 5 +- custom/bin/buildsuper_x64.sh | 3 +- custom/bin/buildsuper_x86.sh | 3 +- custom/generated/command_metadata.h | 458 ++-- .../Contents/Info.plist | 20 + .../Resources/DWARF/metadata_generator | Bin 0 -> 486440 bytes 12 files changed, 2898 insertions(+), 2873 deletions(-) create mode 100644 custom/metadata_generator.dSYM/Contents/Info.plist create mode 100644 custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 4362da1a..235e2813 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -1,613 +1,613 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * ??.??.???? - * - * 4coder development build rule. - * - */ - -// TOP - -//#define FM_PRINT_COMMANDS - -#include "4coder_base_types.h" -#include "4coder_version.h" - -#include "4coder_base_types.cpp" -#include "4coder_malloc_allocator.cpp" - -#define FTECH_FILE_MOVING_IMPLEMENTATION -#include "4coder_file_moving.h" - - -// -// OS and compiler index -// - -enum{ - Platform_Windows, - Platform_Linux, - Platform_Mac, - // - Platform_COUNT, - Platform_None = Platform_COUNT, -}; - -char *platform_names[] = { - "win", - "linux", - "mac", -}; - -enum{ - Compiler_CL, - Compiler_GCC, - // - Compiler_COUNT, - Compiler_None = Compiler_COUNT, -}; - -char *compiler_names[] = { - "cl", - "gcc", -}; - -#if OS_WINDOWS -# define This_OS Platform_Windows -#elif OS_LINUX -# define This_OS Platform_Linux -#elif OS_MAC -# define This_OS Platform_Mac -#else -# error This platform is not enumerated. -#endif - -#if COMPILER_CL -# define This_Compiler Compiler_CL -#elif COMPILER_GCC -# define This_Compiler Compiler_GCC -#else -# error This compilers is not enumerated. -#endif - -// -// Universal directories -// - -#define BUILD_DIR "../build" -#define PACK_DIR "../distributions" -#define SITE_DIR "../site" - -#define FOREIGN "../4coder-non-source/foreign" -#define FOREIGN_WIN "..\\4coder-non-source\\foreign" - -char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; - -// -// Platform layer file tables -// - -char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; -char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed.m", "platform_mac/mac_4ed.cpp", 0 }; - -char **platform_layers[Platform_COUNT] = { - windows_platform_layer, - linux_platform_layer , - mac_platform_layer , -}; - -char *windows_cl_platform_inc[] = { "platform_all", 0 }; -char *linux_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; -char *mac_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; - -char **platform_includes[Platform_COUNT][Compiler_COUNT] = { - {windows_cl_platform_inc, 0 }, - {0 , linux_gcc_platform_inc}, - {0 , mac_gcc_platform_inc }, -}; - -char *default_custom_target = "../code/custom/4coder_default_bindings.cpp"; - -// NOTE(allen): Architectures - -enum{ - Arch_X64, - Arch_X86, - - // - Arch_COUNT, - Arch_None = Arch_COUNT, -}; - -char *arch_names[] = { - "x64", - "x86", -}; - -// NOTE(allen): Build flags - -enum{ - OPTS = 0x1, - LIBS = 0x2, - ICON = 0x4, - SHARED_CODE = 0x8, - DEBUG_INFO = 0x10, - OPTIMIZATION = 0x20, - SUPER = 0x40, - INTERNAL = 0x80, - SHIP = 0x100, -}; - -internal char** -get_defines_from_flags(Arena *arena, u32 flags){ - char **result = 0; - if (HasFlag(flags, SHIP)){ - result = fm_list(arena, fm_list_one_item(arena, "SHIP_MODE"), result); - } - if (HasFlag(flags, INTERNAL)){ - result = fm_list(arena, fm_list_one_item(arena, "FRED_INTERNAL"), result); - } - if (HasFlag(flags, SUPER)){ - result = fm_list(arena, fm_list_one_item(arena, "FRED_SUPER"), result); - } - return(result); -} - -// -// build implementation: cl -// - -#if COMPILER_CL - -#define CL_OPTS \ -"-W4 -wd4310 -wd4100 -wd4201 -wd4505 -wd4996 " \ -"-wd4127 -wd4510 -wd4512 -wd4610 -wd4390 " \ -"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC" - -#define CL_LIBS_X64 \ -"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib " \ -FOREIGN_WIN "\\x64\\freetype.lib" - -#define CL_LIBS_X86 \ -"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib " \ -FOREIGN_WIN "\\x86\\freetype.lib" - -#define CL_ICON "..\\4coder-non-source\\res\\icon.res" - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - Temp_Dir temp = fm_pushdir(out_path); - - Build_Line line; - fm_init_build_line(&line); - - if (arch == Arch_X86){ - fm_add_to_line(line, "%s\\custom\\bin\\setup_cl_x86.bat &", code_path); - } - - fm_add_to_line(line, "cl"); - - if (flags & OPTS){ - fm_add_to_line(line, CL_OPTS); - } - - switch (arch){ - case Arch_X64: fm_add_to_line(line, "-DFTECH_64_BIT"); break; - case Arch_X86: fm_add_to_line(line, "-DFTECH_32_BIT"); break; - default: InvalidPath; - } - - fm_add_to_line(line, "-I%s", code_path); - if (inc_folders != 0){ - for (u32 i = 0; inc_folders[i] != 0; ++i){ - char *str = fm_str(arena, code_path, "/", inc_folders[i]); - fm_add_to_line(line, "-I%s", str); - } - } - - if (flags & LIBS){ - switch (arch){ - case Arch_X64: fm_add_to_line(line, CL_LIBS_X64); break; - case Arch_X86: fm_add_to_line(line, CL_LIBS_X86); break; - default: InvalidPath; - } - } - - if (flags & ICON){ - fm_add_to_line(line, CL_ICON); - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-Zi"); - fm_add_to_line(line, "-DDO_CRAZY_EXPENSIVE_ASSERTS"); - } - - if (flags & OPTIMIZATION){ - fm_add_to_line(line, "-O2"); - } - - if (flags & SHARED_CODE){ - fm_add_to_line(line, "-LD"); - } - - if (defines != 0){ - for (u32 i = 0; defines[i] != 0; ++i){ - char *define_flag = fm_str(arena, "-D", defines[i]); - fm_add_to_line(line, "%s", define_flag); - } - } - - for (u32 i = 0; code_files[i]; ++i){ - fm_add_to_line(line, "\"%s\\%s\"", code_path, code_files[i]); - } - - fm_add_to_line(line, "-Fe%s", out_file); - - fm_add_to_line(line, "-link -INCREMENTAL:NO -RELEASE -PDBALTPATH:%%_PDB%%"); - switch (arch){ - case Arch_X64: fm_add_to_line(line, "-MACHINE:X64"); break; - case Arch_X86: fm_add_to_line(line, "-MACHINE:X86"); break; - default: InvalidPath; - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-DEBUG"); - } - - if (flags & SHARED_CODE){ - Assert(exports != 0); - fm_add_to_line(line, "-OPT:REF"); - for (u32 i = 0; exports[i] != 0; ++i){ - char *str = fm_str(arena, "-EXPORT:", exports[i]); - fm_add_to_line(line, "%s", str); - } - } - else{ - fm_add_to_line(line, "-NODEFAULTLIB:library"); - } - - fm_finish_build_line(&line); - - //printf("%s\n", line.build_options); - systemf("%s", line.build_options); - fm_popdir(temp); - - fflush(stdout); -} - -// -// build implementation: gcc -// - -#elif COMPILER_GCC - -#if OS_LINUX - -# define GCC_OPTS \ -"-Wno-write-strings " \ -"-D_GNU_SOURCE -fPIC " \ -"-fno-threadsafe-statics -pthread " \ -"-Wno-unused-result" - -#define GCC_LIBS_COMMON \ -"-lX11 -lpthread -lm -lrt " \ -"-lGL -ldl -lXfixes -lfreetype -lfontconfig" - -#define GCC_LIBS_X64 GCC_LIBS_COMMON -#define GCC_LIBS_X86 GCC_LIBS_COMMON - -#elif OS_MAC - -# define GCC_OPTS \ -"-Wno-write-strings -Wno-deprecated-declarations " \ -"-Wno-comment -Wno-switch -Wno-null-dereference " \ -"-Wno-tautological-compare " \ -"-Wno-unused-result " - -#define GCC_LIBS_COMMON \ -"-framework Cocoa -framework QuartzCore " \ -"-framework CoreServices " \ -"-framework OpenGL -framework IOKit " - -#define GCC_LIBS_X64 GCC_LIBS_COMMON \ -FOREIGN "/x64/libfreetype-mac.a" - -#define GCC_LIBS_X86 GCC_LIBS_COMMON \ -FOREIGN "/x86/libfreetype-mac.a" - -#else -# error gcc options not set for this platform -#endif - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - Build_Line line; - fm_init_build_line(&line); - - switch (arch){ - case Arch_X64: - fm_add_to_line(line, "-m64"); - fm_add_to_line(line, "-DFTECH_64_BIT"); break; - - case Arch_X86: - fm_add_to_line(line, "-m32"); - fm_add_to_line(line, "-DFTECH_32_BIT"); break; - - default: InvalidPath; - } - - if (flags & OPTS){ - fm_add_to_line(line, GCC_OPTS); - } - - fm_add_to_line(line, "-I%s", code_path); - if (inc_folders != 0){ - for (u32 i = 0; inc_folders[i] != 0; ++i){ - char *str = fm_str(arena, code_path, "/", inc_folders[i]); - fm_add_to_line(line, "-I%s", str); - } - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-g -O0"); - } - - if (flags & OPTIMIZATION){ - fm_add_to_line(line, "-O3"); - } - - if (flags & SHARED_CODE){ - fm_add_to_line(line, "-shared"); - } - - if (defines != 0){ - for (u32 i = 0; defines[i]; ++i){ - char *define_flag = fm_str(arena, "-D", defines[i]); - fm_add_to_line(line, "%s", define_flag); - } - } - - fm_add_to_line(line, "-I\"%s\"", code_path); - for (u32 i = 0; code_files[i] != 0; ++i){ - fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]); - } - - if (flags & LIBS){ - if (arch == Arch_X64){ - fm_add_to_line(line, GCC_LIBS_X64); - } - else if (arch == Arch_X86) - { - fm_add_to_line(line, GCC_LIBS_X86); - } - } - - fm_finish_build_line(&line); - - Temp_Dir temp = fm_pushdir(out_path); - systemf("g++ %s -o %s", line.build_options, out_file); - fm_popdir(temp); -} - -#else -# error build function not defined for this compiler -#endif - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char *code_file, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - char **code_files = fm_list_one_item(arena, code_file); - build(arena, flags, arch, code_path, code_files, out_path, out_file, defines, exports, inc_folders); -} - -internal void -build_and_run(Arena *arena, char *cdir, char *filename, char *name, u32 flags){ - char *dir = fm_str(arena, BUILD_DIR); - - { - char *file = fm_str(arena, filename); - BEGIN_TIME_SECTION(); - build(arena, flags, Arch_X64, cdir, file, dir, name, get_defines_from_flags(arena, flags), 0, includes); - END_TIME_SECTION(fm_str(arena, "build ", name)); - } - - if (prev_error == 0){ - char *cmd = fm_str(arena, dir, "/", name); - BEGIN_TIME_SECTION(); - fm_execute_in_dir(cdir, cmd, 0); - END_TIME_SECTION(fm_str(arena, "run ", name)); - } -} - -internal void -buildsuper(Arena *arena, char *cdir, char *file, u32 arch){ - printf("BUILDSUPER: cdir: %s; file: %s; arch: %u\n", cdir, file, arch); - - BEGIN_TIME_SECTION(); - Temp_Dir temp = fm_pushdir(fm_str(arena, BUILD_DIR)); - - char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], BAT); - - char *build_command = fm_str(arena, "\"", cdir, "/", build_script, "\" \"", file, "\""); - if (This_OS == Platform_Windows){ - build_command = fm_str(arena, "call ", build_command); - } - systemf("%s", build_command); - - fm_popdir(temp); - END_TIME_SECTION("build custom"); - fflush(stdout); -} - -internal void -build_main(Arena *arena, char *cdir, b32 update_local_theme, u32 flags, u32 arch){ - char *dir = fm_str(arena, BUILD_DIR); - - { - char *file = fm_str(arena, "4ed_app_target.cpp"); - char **exports = fm_list_one_item(arena, "app_get_functions"); - - char **build_includes = includes; - - BEGIN_TIME_SECTION(); - build(arena, OPTS | SHARED_CODE | flags, arch, cdir, file, dir, "4ed_app" DLL, get_defines_from_flags(arena, flags), exports, build_includes); - END_TIME_SECTION("build 4ed_app"); - } - - { - BEGIN_TIME_SECTION(); - char **inc = (char**)fm_list(arena, includes, platform_includes[This_OS][This_Compiler]); - build(arena, OPTS | LIBS | ICON | flags, arch, cdir, platform_layers[This_OS], dir, "4ed", get_defines_from_flags(arena, flags), 0, inc); - END_TIME_SECTION("build 4ed"); - } - - if (update_local_theme){ - BEGIN_TIME_SECTION(); - char *themes_folder = fm_str(arena, "../build/themes"); - char *source_themes_folder = fm_str(arena, "ship_files/themes"); - fm_clear_folder(themes_folder); - fm_make_folder_if_missing(arena, themes_folder); - fm_copy_all(source_themes_folder, themes_folder); - END_TIME_SECTION("move files"); - } - - fflush(stdout); -} - -internal void -standard_build(Arena *arena, char *cdir, u32 flags, u32 arch){ - buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch); - build_main(arena, cdir, true, flags, arch); -} - -internal char* -get_4coder_dist_name(Arena *arena, u32 platform, char *tier, u32 arch){ - char *name = fm_str(arena, "4coder-" MAJOR_STR "-" MINOR_STR "-" PATCH_STR "-", tier); - if (platform != Platform_None){ - name = fm_str(arena, name, "-", platform_names[platform]); - } - if (arch != Arch_None){ - name = fm_str(arena, name, "-", arch_names[arch]); - } - return(name); -} - -enum{ - Tier_Demo, - Tier_Super, - Tier_COUNT, -}; - -internal void -package(Arena *arena, char *cdir){ - // NOTE(allen): meta - char *build_dir = fm_str(arena, BUILD_DIR); - char *pack_dir = fm_str(arena, PACK_DIR); - char *dist_files[3]; - dist_files[0] = fm_str(arena, "../4coder-non-source/dist_files"); - dist_files[1] = fm_str(arena, "ship_files"); - dist_files[2] = fm_str(arena, "ship_files_super"); - - printf("build dir: %s\n", build_dir); - printf("pack dir: %s\n", pack_dir); - printf("dist files: %s, %s, %s\n", dist_files[0], dist_files[1], dist_files[2]); - fflush(stdout); - - char *tier_names[] = { "demo", "super", }; - u32 base_flags = SHIP | DEBUG_INFO | OPTIMIZATION; - u32 tier_flags[] = { 0, SUPER, }; - - fm_make_folder_if_missing(arena, pack_dir); - - for (u32 i = 0; i < Tier_COUNT; i += 1){ - char *tier_name = tier_names[i]; - u32 flags = base_flags | tier_flags[i]; - - Temp_Memory temp = begin_temp(arena); - char *current_dist_tier = fm_str(arena, ".." SLASH "current_dist_", tier_name); - - for (u32 arch = 0; arch < Arch_COUNT; ++arch){ - char *arch_name = arch_names[arch]; - char *parent_dir = fm_str(arena, current_dist_tier, "_", arch_name); - char *dir = fm_str(arena, parent_dir, SLASH "4coder"); - char *zip_dir = fm_str(arena, pack_dir, SLASH, tier_name, "_", arch_name); - - printf("\nbuild: %s_%s\n", tier_name, arch_name); - printf("parent_dir: %s\n", parent_dir); - printf("dir: %s\n", dir); - printf("zip_dir: %s\n", zip_dir); - fflush(stdout); - - buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch); - build_main(arena, cdir, false, flags, arch); - - fm_make_folder_if_missing(arena, parent_dir); - fm_clear_folder(parent_dir); - fm_make_folder_if_missing(arena, dir); - fm_copy_file(fm_str(arena, build_dir, "/4ed" EXE), fm_str(arena, dir, "/4ed" EXE)); - fm_copy_file(fm_str(arena, build_dir, "/4ed_app" DLL), fm_str(arena, dir, "/4ed_app" DLL)); - fm_copy_file(fm_str(arena, build_dir, "/custom_4coder" DLL), fm_str(arena, dir, "/custom_4coder" DLL)); - - i32 dist_file_count = ArrayCount(dist_files); - if (i == Tier_Demo){ - dist_file_count -= 1; - } - - for (i32 j = 0; j < dist_file_count; j += 1){ - fm_copy_all(dist_files[j], dir); - } - - if (i == Tier_Super){ - char *custom_src_dir = fm_str(arena, cdir, SLASH, "custom"); - char *custom_dst_dir = fm_str(arena, dir, SLASH, "custom"); - fm_make_folder_if_missing(arena, custom_dst_dir); - fm_copy_all(custom_src_dir, custom_dst_dir); - } - - char *dist_name = get_4coder_dist_name(arena, This_OS, tier_name, arch); - char *zip_name = fm_str(arena, zip_dir, SLASH, dist_name, ".zip"); - fm_make_folder_if_missing(arena, zip_dir); - fm_zip(parent_dir, "4coder", zip_name); - } - - end_temp(temp); - } -} - -int main(int argc, char **argv){ - Arena arena = fm_init_system(); - - char cdir[256]; - BEGIN_TIME_SECTION(); - i32 n = fm_get_current_directory(cdir, sizeof(cdir)); - Assert(n < sizeof(cdir)); - END_TIME_SECTION("current directory"); - - u32 flags = SUPER; - u32 arch = Arch_X64; - #if defined(DEV_BUILD) || defined(DEV_BUILD_X86) - flags |= DEBUG_INFO | INTERNAL; - #endif -#if defined(OPT_BUILD) || defined(OPT_BUILD_X86) - flags |= OPTIMIZATION; - #endif -#if defined(DEV_BUILD_X86) || defined(OPT_BUILD_X86) - arch = Arch_X86; -#endif - -#if defined(DEV_BUILD) || defined(OPT_BUILD) || defined(DEV_BUILD_X86) -standard_build(&arena, cdir, flags, arch); - -#elif defined(PACKAGE) - package(&arena, cdir); - -#else -# error No build type specified. -#endif - - return(error_state); -} - -// BOTTOM - +/* + * Mr. 4th Dimention - Allen Webster + * + * ??.??.???? + * + * 4coder development build rule. + * + */ + +// TOP + +//#define FM_PRINT_COMMANDS + +#include "4coder_base_types.h" +#include "4coder_version.h" + +#include "4coder_base_types.cpp" +#include "4coder_malloc_allocator.cpp" + +#define FTECH_FILE_MOVING_IMPLEMENTATION +#include "4coder_file_moving.h" + + +// +// OS and compiler index +// + +enum{ + Platform_Windows, + Platform_Linux, + Platform_Mac, + // + Platform_COUNT, + Platform_None = Platform_COUNT, +}; + +char *platform_names[] = { + "win", + "linux", + "mac", +}; + +enum{ + Compiler_CL, + Compiler_GCC, + // + Compiler_COUNT, + Compiler_None = Compiler_COUNT, +}; + +char *compiler_names[] = { + "cl", + "gcc", +}; + +#if OS_WINDOWS +# define This_OS Platform_Windows +#elif OS_LINUX +# define This_OS Platform_Linux +#elif OS_MAC +# define This_OS Platform_Mac +#else +# error This platform is not enumerated. +#endif + +#if COMPILER_CL +# define This_Compiler Compiler_CL +#elif COMPILER_GCC +# define This_Compiler Compiler_GCC +#else +# error This compilers is not enumerated. +#endif + +// +// Universal directories +// + +#define BUILD_DIR "../build" +#define PACK_DIR "../distributions" +#define SITE_DIR "../site" + +#define FOREIGN "../4coder-non-source/foreign" +#define FOREIGN_WIN "..\\4coder-non-source\\foreign" + +char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; + +// +// Platform layer file tables +// + +char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; +char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; +char *mac_platform_layer[] = { "platform_mac/mac_4ed.m", "platform_mac/mac_4ed.cpp", 0 }; + +char **platform_layers[Platform_COUNT] = { + windows_platform_layer, + linux_platform_layer , + mac_platform_layer , +}; + +char *windows_cl_platform_inc[] = { "platform_all", 0 }; +char *linux_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; +char *mac_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; + +char **platform_includes[Platform_COUNT][Compiler_COUNT] = { + {windows_cl_platform_inc, 0 }, + {0 , linux_gcc_platform_inc}, + {0 , mac_gcc_platform_inc }, +}; + +char *default_custom_target = "../code/custom/4coder_default_bindings.cpp"; + +// NOTE(allen): Architectures + +enum{ + Arch_X64, + Arch_X86, + + // + Arch_COUNT, + Arch_None = Arch_COUNT, +}; + +char *arch_names[] = { + "x64", + "x86", +}; + +// NOTE(allen): Build flags + +enum{ + OPTS = 0x1, + LIBS = 0x2, + ICON = 0x4, + SHARED_CODE = 0x8, + DEBUG_INFO = 0x10, + OPTIMIZATION = 0x20, + SUPER = 0x40, + INTERNAL = 0x80, + SHIP = 0x100, +}; + +internal char** +get_defines_from_flags(Arena *arena, u32 flags){ + char **result = 0; + if (HasFlag(flags, SHIP)){ + result = fm_list(arena, fm_list_one_item(arena, "SHIP_MODE"), result); + } + if (HasFlag(flags, INTERNAL)){ + result = fm_list(arena, fm_list_one_item(arena, "FRED_INTERNAL"), result); + } + if (HasFlag(flags, SUPER)){ + result = fm_list(arena, fm_list_one_item(arena, "FRED_SUPER"), result); + } + return(result); +} + +// +// build implementation: cl +// + +#if COMPILER_CL + +#define CL_OPTS \ +"-W4 -wd4310 -wd4100 -wd4201 -wd4505 -wd4996 " \ +"-wd4127 -wd4510 -wd4512 -wd4610 -wd4390 " \ +"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC" + +#define CL_LIBS_X64 \ +"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib " \ +FOREIGN_WIN "\\x64\\freetype.lib" + +#define CL_LIBS_X86 \ +"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib " \ +FOREIGN_WIN "\\x86\\freetype.lib" + +#define CL_ICON "..\\4coder-non-source\\res\\icon.res" + +internal void +build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ + Temp_Dir temp = fm_pushdir(out_path); + + Build_Line line; + fm_init_build_line(&line); + + if (arch == Arch_X86){ + fm_add_to_line(line, "%s\\custom\\bin\\setup_cl_x86.bat &", code_path); + } + + fm_add_to_line(line, "cl"); + + if (flags & OPTS){ + fm_add_to_line(line, CL_OPTS); + } + + switch (arch){ + case Arch_X64: fm_add_to_line(line, "-DFTECH_64_BIT"); break; + case Arch_X86: fm_add_to_line(line, "-DFTECH_32_BIT"); break; + default: InvalidPath; + } + + fm_add_to_line(line, "-I%s", code_path); + if (inc_folders != 0){ + for (u32 i = 0; inc_folders[i] != 0; ++i){ + char *str = fm_str(arena, code_path, "/", inc_folders[i]); + fm_add_to_line(line, "-I%s", str); + } + } + + if (flags & LIBS){ + switch (arch){ + case Arch_X64: fm_add_to_line(line, CL_LIBS_X64); break; + case Arch_X86: fm_add_to_line(line, CL_LIBS_X86); break; + default: InvalidPath; + } + } + + if (flags & ICON){ + fm_add_to_line(line, CL_ICON); + } + + if (flags & DEBUG_INFO){ + fm_add_to_line(line, "-Zi"); + fm_add_to_line(line, "-DDO_CRAZY_EXPENSIVE_ASSERTS"); + } + + if (flags & OPTIMIZATION){ + fm_add_to_line(line, "-O2"); + } + + if (flags & SHARED_CODE){ + fm_add_to_line(line, "-LD"); + } + + if (defines != 0){ + for (u32 i = 0; defines[i] != 0; ++i){ + char *define_flag = fm_str(arena, "-D", defines[i]); + fm_add_to_line(line, "%s", define_flag); + } + } + + for (u32 i = 0; code_files[i]; ++i){ + fm_add_to_line(line, "\"%s\\%s\"", code_path, code_files[i]); + } + + fm_add_to_line(line, "-Fe%s", out_file); + + fm_add_to_line(line, "-link -INCREMENTAL:NO -RELEASE -PDBALTPATH:%%_PDB%%"); + switch (arch){ + case Arch_X64: fm_add_to_line(line, "-MACHINE:X64"); break; + case Arch_X86: fm_add_to_line(line, "-MACHINE:X86"); break; + default: InvalidPath; + } + + if (flags & DEBUG_INFO){ + fm_add_to_line(line, "-DEBUG"); + } + + if (flags & SHARED_CODE){ + Assert(exports != 0); + fm_add_to_line(line, "-OPT:REF"); + for (u32 i = 0; exports[i] != 0; ++i){ + char *str = fm_str(arena, "-EXPORT:", exports[i]); + fm_add_to_line(line, "%s", str); + } + } + else{ + fm_add_to_line(line, "-NODEFAULTLIB:library"); + } + + fm_finish_build_line(&line); + + //printf("%s\n", line.build_options); + systemf("%s", line.build_options); + fm_popdir(temp); + + fflush(stdout); +} + +// +// build implementation: gcc +// + +#elif COMPILER_GCC + +#if OS_LINUX + +# define GCC_OPTS \ +"-Wno-write-strings " \ +"-D_GNU_SOURCE -fPIC " \ +"-fno-threadsafe-statics -pthread " \ +"-Wno-unused-result" + +#define GCC_LIBS_COMMON \ +"-lX11 -lpthread -lm -lrt " \ +"-lGL -ldl -lXfixes -lfreetype -lfontconfig" + +#define GCC_LIBS_X64 GCC_LIBS_COMMON +#define GCC_LIBS_X86 GCC_LIBS_COMMON + +#elif OS_MAC + +# define GCC_OPTS \ +"-Wno-write-strings -Wno-deprecated-declarations " \ +"-Wno-comment -Wno-switch -Wno-null-dereference " \ +"-Wno-tautological-compare " \ +"-Wno-unused-result " + +#define GCC_LIBS_COMMON \ +"-framework Cocoa -framework QuartzCore " \ +"-framework CoreServices " \ +"-framework OpenGL -framework IOKit " + +#define GCC_LIBS_X64 GCC_LIBS_COMMON \ +FOREIGN "/x64/libfreetype-mac.a" + +#define GCC_LIBS_X86 GCC_LIBS_COMMON \ +FOREIGN "/x86/libfreetype-mac.a" + +#else +# error gcc options not set for this platform +#endif + +internal void +build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ + Build_Line line; + fm_init_build_line(&line); + + switch (arch){ + case Arch_X64: + fm_add_to_line(line, "-m64"); + fm_add_to_line(line, "-DFTECH_64_BIT"); break; + + case Arch_X86: + fm_add_to_line(line, "-m32"); + fm_add_to_line(line, "-DFTECH_32_BIT"); break; + + default: InvalidPath; + } + + if (flags & OPTS){ + fm_add_to_line(line, GCC_OPTS); + } + + fm_add_to_line(line, "-I%s", code_path); + if (inc_folders != 0){ + for (u32 i = 0; inc_folders[i] != 0; ++i){ + char *str = fm_str(arena, code_path, "/", inc_folders[i]); + fm_add_to_line(line, "-I%s", str); + } + } + + if (flags & DEBUG_INFO){ + fm_add_to_line(line, "-g -O0"); + } + + if (flags & OPTIMIZATION){ + fm_add_to_line(line, "-O3"); + } + + if (flags & SHARED_CODE){ + fm_add_to_line(line, "-shared"); + } + + if (defines != 0){ + for (u32 i = 0; defines[i]; ++i){ + char *define_flag = fm_str(arena, "-D", defines[i]); + fm_add_to_line(line, "%s", define_flag); + } + } + + fm_add_to_line(line, "-I\"%s\"", code_path); + for (u32 i = 0; code_files[i] != 0; ++i){ + fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]); + } + + if (flags & LIBS){ + if (arch == Arch_X64){ + fm_add_to_line(line, GCC_LIBS_X64); + } + else if (arch == Arch_X86) + { + fm_add_to_line(line, GCC_LIBS_X86); + } + } + + fm_finish_build_line(&line); + + Temp_Dir temp = fm_pushdir(out_path); + systemf("g++ %s -o %s", line.build_options, out_file); + fm_popdir(temp); +} + +#else +# error build function not defined for this compiler +#endif + +internal void +build(Arena *arena, u32 flags, u32 arch, char *code_path, char *code_file, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ + char **code_files = fm_list_one_item(arena, code_file); + build(arena, flags, arch, code_path, code_files, out_path, out_file, defines, exports, inc_folders); +} + +internal void +build_and_run(Arena *arena, char *cdir, char *filename, char *name, u32 flags){ + char *dir = fm_str(arena, BUILD_DIR); + + { + char *file = fm_str(arena, filename); + BEGIN_TIME_SECTION(); + build(arena, flags, Arch_X64, cdir, file, dir, name, get_defines_from_flags(arena, flags), 0, includes); + END_TIME_SECTION(fm_str(arena, "build ", name)); + } + + if (prev_error == 0){ + char *cmd = fm_str(arena, dir, "/", name); + BEGIN_TIME_SECTION(); + fm_execute_in_dir(cdir, cmd, 0); + END_TIME_SECTION(fm_str(arena, "run ", name)); + } +} + +internal void +buildsuper(Arena *arena, char *cdir, char *file, u32 arch){ + printf("BUILDSUPER: cdir: %s; file: %s; arch: %u\n", cdir, file, arch); + + BEGIN_TIME_SECTION(); + Temp_Dir temp = fm_pushdir(fm_str(arena, BUILD_DIR)); + + char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], BAT); + + char *build_command = fm_str(arena, "\"", cdir, "/", build_script, "\" \"", file, "\""); + if (This_OS == Platform_Windows){ + build_command = fm_str(arena, "call ", build_command); + } + systemf("%s", build_command); + + fm_popdir(temp); + END_TIME_SECTION("build custom"); + fflush(stdout); +} + +internal void +build_main(Arena *arena, char *cdir, b32 update_local_theme, u32 flags, u32 arch){ + char *dir = fm_str(arena, BUILD_DIR); + + { + char *file = fm_str(arena, "4ed_app_target.cpp"); + char **exports = fm_list_one_item(arena, "app_get_functions"); + + char **build_includes = includes; + + BEGIN_TIME_SECTION(); + build(arena, OPTS | SHARED_CODE | flags, arch, cdir, file, dir, "4ed_app" DLL, get_defines_from_flags(arena, flags), exports, build_includes); + END_TIME_SECTION("build 4ed_app"); + } + + { + BEGIN_TIME_SECTION(); + char **inc = (char**)fm_list(arena, includes, platform_includes[This_OS][This_Compiler]); + build(arena, OPTS | LIBS | ICON | flags, arch, cdir, platform_layers[This_OS], dir, "4ed", get_defines_from_flags(arena, flags), 0, inc); + END_TIME_SECTION("build 4ed"); + } + + if (update_local_theme){ + BEGIN_TIME_SECTION(); + char *themes_folder = fm_str(arena, "../build/themes"); + char *source_themes_folder = fm_str(arena, "ship_files/themes"); + fm_clear_folder(themes_folder); + fm_make_folder_if_missing(arena, themes_folder); + fm_copy_all(source_themes_folder, themes_folder); + END_TIME_SECTION("move files"); + } + + fflush(stdout); +} + +internal void +standard_build(Arena *arena, char *cdir, u32 flags, u32 arch){ + buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch); + build_main(arena, cdir, true, flags, arch); +} + +internal char* +get_4coder_dist_name(Arena *arena, u32 platform, char *tier, u32 arch){ + char *name = fm_str(arena, "4coder-" MAJOR_STR "-" MINOR_STR "-" PATCH_STR "-", tier); + if (platform != Platform_None){ + name = fm_str(arena, name, "-", platform_names[platform]); + } + if (arch != Arch_None){ + name = fm_str(arena, name, "-", arch_names[arch]); + } + return(name); +} + +enum{ + Tier_Demo, + Tier_Super, + Tier_COUNT, +}; + +internal void +package(Arena *arena, char *cdir){ + // NOTE(allen): meta + char *build_dir = fm_str(arena, BUILD_DIR); + char *pack_dir = fm_str(arena, PACK_DIR); + char *dist_files[3]; + dist_files[0] = fm_str(arena, "../4coder-non-source/dist_files"); + dist_files[1] = fm_str(arena, "ship_files"); + dist_files[2] = fm_str(arena, "ship_files_super"); + + printf("build dir: %s\n", build_dir); + printf("pack dir: %s\n", pack_dir); + printf("dist files: %s, %s, %s\n", dist_files[0], dist_files[1], dist_files[2]); + fflush(stdout); + + char *tier_names[] = { "demo", "super", }; + u32 base_flags = SHIP | DEBUG_INFO | OPTIMIZATION; + u32 tier_flags[] = { 0, SUPER, }; + + fm_make_folder_if_missing(arena, pack_dir); + + for (u32 i = 0; i < Tier_COUNT; i += 1){ + char *tier_name = tier_names[i]; + u32 flags = base_flags | tier_flags[i]; + + Temp_Memory temp = begin_temp(arena); + char *current_dist_tier = fm_str(arena, ".." SLASH "current_dist_", tier_name); + + for (u32 arch = 0; arch < Arch_COUNT; ++arch){ + char *arch_name = arch_names[arch]; + char *parent_dir = fm_str(arena, current_dist_tier, "_", arch_name); + char *dir = fm_str(arena, parent_dir, SLASH "4coder"); + char *zip_dir = fm_str(arena, pack_dir, SLASH, tier_name, "_", arch_name); + + printf("\nbuild: %s_%s\n", tier_name, arch_name); + printf("parent_dir: %s\n", parent_dir); + printf("dir: %s\n", dir); + printf("zip_dir: %s\n", zip_dir); + fflush(stdout); + + buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch); + build_main(arena, cdir, false, flags, arch); + + fm_make_folder_if_missing(arena, parent_dir); + fm_clear_folder(parent_dir); + fm_make_folder_if_missing(arena, dir); + fm_copy_file(fm_str(arena, build_dir, "/4ed" EXE), fm_str(arena, dir, "/4ed" EXE)); + fm_copy_file(fm_str(arena, build_dir, "/4ed_app" DLL), fm_str(arena, dir, "/4ed_app" DLL)); + fm_copy_file(fm_str(arena, build_dir, "/custom_4coder" DLL), fm_str(arena, dir, "/custom_4coder" DLL)); + + i32 dist_file_count = ArrayCount(dist_files); + if (i == Tier_Demo){ + dist_file_count -= 1; + } + + for (i32 j = 0; j < dist_file_count; j += 1){ + fm_copy_all(dist_files[j], dir); + } + + if (i == Tier_Super){ + char *custom_src_dir = fm_str(arena, cdir, SLASH, "custom"); + char *custom_dst_dir = fm_str(arena, dir, SLASH, "custom"); + fm_make_folder_if_missing(arena, custom_dst_dir); + fm_copy_all(custom_src_dir, custom_dst_dir); + } + + char *dist_name = get_4coder_dist_name(arena, This_OS, tier_name, arch); + char *zip_name = fm_str(arena, zip_dir, SLASH, dist_name, ".zip"); + fm_make_folder_if_missing(arena, zip_dir); + fm_zip(parent_dir, "4coder", zip_name); + } + + end_temp(temp); + } +} + +int main(int argc, char **argv){ + Arena arena = fm_init_system(); + + char cdir[256]; + BEGIN_TIME_SECTION(); + i32 n = fm_get_current_directory(cdir, sizeof(cdir)); + Assert(n < sizeof(cdir)); + END_TIME_SECTION("current directory"); + + u32 flags = SUPER; + u32 arch = Arch_X64; +#if defined(DEV_BUILD) || defined(DEV_BUILD_X86) + flags |= DEBUG_INFO | INTERNAL; +#endif +#if defined(OPT_BUILD) || defined(OPT_BUILD_X86) + flags |= OPTIMIZATION; +#endif +#if defined(DEV_BUILD_X86) || defined(OPT_BUILD_X86) + arch = Arch_X86; +#endif + +#if defined(DEV_BUILD) || defined(OPT_BUILD) || defined(DEV_BUILD_X86) + standard_build(&arena, cdir, flags, arch); + +#elif defined(PACKAGE) + package(&arena, cdir); + +#else +# error No build type specified. +#endif + + return(error_state); +} + +// BOTTOM + diff --git a/bin/build.sh b/bin/build.sh index 0d01ab2d..491067b0 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -4,7 +4,8 @@ set -e # Set up directories (mirrors build.bat) -ME="$(readlink -f "$0")" +# NOTE(yuval): Temporary fix that works only for macOS +ME="$(greadlink -f "$0")" LOCATION="$(dirname "$ME")" SRC_ROOT="$(dirname "$LOCATION")" PROJECT_ROOT="$(dirname "$SRC_ROOT")" @@ -29,7 +30,7 @@ os=$("$BIN_ROOT/detect_os.sh") if [[ "$os" == "linux" ]]; then WARNINGS="-Wno-write-strings -Wno-comment" elif [[ "$os" == "mac" ]]; then -WARNINGS="-Wno-write-strings -Wno-comment -Wno-logical-op-parentheses -Wno-null-dereference -Wno-switch" +WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-switch" fi FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" diff --git a/custom/4coder_base_types.cpp b/custom/4coder_base_types.cpp index 9f7b3483..2209ff55 100644 --- a/custom/4coder_base_types.cpp +++ b/custom/4coder_base_types.cpp @@ -1727,7 +1727,7 @@ Ii32_size(i32 pos, i32 size){ function Range_i64 Ii64_size(i64 pos, i64 size){ return(Ii64(pos, pos + size)); -} +} function Range_u64 Iu64_size(u64 pos, u64 size){ return(Iu64(pos, pos + size)); @@ -2978,7 +2978,7 @@ make_base_allocator(Base_Allocator_Reserve_Signature *func_reserve, } function Data base_allocate__inner(Base_Allocator *allocator, u64 size, String_Const_u8 location){ - u64 full_size = 0; + u64 full_size = 0; void *memory = allocator->reserve(allocator->user_data, size, &full_size, location); allocator->commit(allocator->user_data, memory, full_size); return(make_data(memory, (u64)full_size)); @@ -5281,7 +5281,7 @@ push_string_copy(Arena *arena, u64 size, String_Const_Any src){ return(string); } - function String_Const_u8_Array +function String_Const_u8_Array push_string_array_copy(Arena *arena, String_Const_u8_Array src){ String_Const_u8_Array result = {}; result.vals = push_array(arena, String_Const_u8, src.count); @@ -6943,10 +6943,10 @@ global_const u8 integer_symbol_reverse[128] = { global_const u8 base64[64] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '$', }; diff --git a/custom/4coder_code_index.cpp b/custom/4coder_code_index.cpp index 2e7d0fcc..e3237f92 100644 --- a/custom/4coder_code_index.cpp +++ b/custom/4coder_code_index.cpp @@ -1,1173 +1,1173 @@ -/* -4coder_code_index.cpp - Generic code indexing system for layout, definition jumps, etc. -*/ - -// TOP - -global Code_Index global_code_index = {}; - -function void -code_index_init(void){ - global_code_index.mutex = system_mutex_make(); - global_code_index.node_arena = make_arena_system(KB(4)); - global_code_index.buffer_to_index_file = make_table_u64_u64(global_code_index.node_arena.base_allocator, 500); -} - -function Code_Index_File_Storage* -code_index__alloc_storage(void){ - Code_Index_File_Storage *result = global_code_index.free_storage; - if (result == 0){ - result = push_array_zero(&global_code_index.node_arena, Code_Index_File_Storage, 1); - } - else{ - sll_stack_pop(global_code_index.free_storage); - } - zdll_push_back(global_code_index.storage_first, global_code_index.storage_last, result); - global_code_index.storage_count += 1; - return(result); -} - -function void -code_index__free_storage(Code_Index_File_Storage *storage){ - zdll_remove(global_code_index.storage_first, global_code_index.storage_last, storage); - global_code_index.storage_count -= 1; - sll_stack_push(global_code_index.free_storage, storage); -} - -function void -code_index_push_nest(Code_Index_Nest_List *list, Code_Index_Nest *nest){ - sll_queue_push(list->first, list->last, nest); - list->count += 1; -} - -function Code_Index_Nest_Ptr_Array -code_index_nest_ptr_array_from_list(Arena *arena, Code_Index_Nest_List *list){ - Code_Index_Nest_Ptr_Array array = {}; - array.ptrs = push_array_zero(arena, Code_Index_Nest*, list->count); - array.count = list->count; - i32 counter = 0; - for (Code_Index_Nest *node = list->first; - node != 0; - node = node->next){ - array.ptrs[counter] = node; - counter += 1; - } - return(array); -} - -function Code_Index_Note_Ptr_Array -code_index_note_ptr_array_from_list(Arena *arena, Code_Index_Note_List *list){ - Code_Index_Note_Ptr_Array array = {}; - array.ptrs = push_array_zero(arena, Code_Index_Note*, list->count); - array.count = list->count; - i32 counter = 0; - for (Code_Index_Note *node = list->first; - node != 0; - node = node->next){ - array.ptrs[counter] = node; - counter += 1; - } - return(array); -} - -function void -code_index_lock(void){ - system_mutex_acquire(global_code_index.mutex); -} - -function void -code_index_unlock(void){ - system_mutex_release(global_code_index.mutex); -} - -function void -code_index_set_file(Buffer_ID buffer, Arena arena, Code_Index_File *index){ - Code_Index_File_Storage *storage = 0; - Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); - if (lookup.found_match){ - u64 val = 0; - table_read(&global_code_index.buffer_to_index_file, lookup, &val); - storage = (Code_Index_File_Storage*)IntAsPtr(val); - linalloc_clear(&storage->arena); - } - else{ - storage = code_index__alloc_storage(); - table_insert(&global_code_index.buffer_to_index_file, buffer, (u64)PtrAsInt(storage)); - } - storage->arena = arena; - storage->file = index; -} - -function void -code_index_erase_file(Buffer_ID buffer){ - Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); - if (lookup.found_match){ - u64 val = 0; - table_read(&global_code_index.buffer_to_index_file, lookup, &val); - Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); - linalloc_clear(&storage->arena); - table_erase(&global_code_index.buffer_to_index_file, lookup); - code_index__free_storage(storage); - } -} - -function Code_Index_File* -code_index_get_file(Buffer_ID buffer){ - Code_Index_File *result = 0; - Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); - if (lookup.found_match){ - u64 val = 0; - table_read(&global_code_index.buffer_to_index_file, lookup, &val); - Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); - result = storage->file; - } - return(result); -} - -function Code_Index_Nest* -code_index_get_nest(Code_Index_Nest_Ptr_Array *array, i64 pos){ - Code_Index_Nest *result = 0; - i32 count = array->count; - Code_Index_Nest **nest_ptrs = array->ptrs; - for (i32 i = 0; i < count; i += 1){ - Code_Index_Nest *nest = nest_ptrs[i]; - if (nest->open.max <= pos && pos <= nest->close.min){ - Code_Index_Nest *sub_nest = - code_index_get_nest(&nest->nest_array, pos); - if (sub_nest != 0){ - result = sub_nest; - } - else{ - result = nest; - } - break; - } - } - return(result); -} - -function Code_Index_Nest* -code_index_get_nest(Code_Index_Nest *nest, i64 pos){ - return(code_index_get_nest(&nest->nest_array, pos)); -} - -function Code_Index_Nest* -code_index_get_nest(Code_Index_File *file, i64 pos){ - return(code_index_get_nest(&file->nest_array, pos)); -} - -function void -index_shift(i64 *ptr, Range_i64 old_range, u64 new_size){ - i64 i = *ptr; - if (old_range.min <= i && i < old_range.max){ - *ptr = old_range.first; - } - else if (old_range.max <= i){ - *ptr = i + new_size - (old_range.max - old_range.min); - } -} - -function void -code_index_shift(Code_Index_Nest_Ptr_Array *array, - Range_i64 old_range, u64 new_size){ - i32 count = array->count; - Code_Index_Nest **nest_ptr = array->ptrs; - for (i32 i = 0; i < count; i += 1, nest_ptr += 1){ - Code_Index_Nest *nest = *nest_ptr; - index_shift(&nest->open.min, old_range, new_size); - index_shift(&nest->open.max, old_range, new_size); - if (nest->is_closed){ - index_shift(&nest->close.min, old_range, new_size); - index_shift(&nest->close.max, old_range, new_size); - } - code_index_shift(&nest->nest_array, old_range, new_size); - } -} - -function void -code_index_shift(Code_Index_File *file, Range_i64 old_range, u64 new_size){ - code_index_shift(&file->nest_array, old_range, new_size); -} - -//////////////////////////////// - -function void -generic_parse_inc(Generic_Parse_State *state){ - if (!token_it_inc_all(&state->it)){ - state->finished = true; - } -} - -function void -generic_parse_skip_soft_tokens(Code_Index_File *index, Generic_Parse_State *state){ - Token *token = token_it_read(&state->it); - for (;token != 0 && !state->finished;){ - if (state->in_preprocessor && !HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){ - break; - } - if (token->kind == TokenBaseKind_Comment){ - state->handle_comment(state->app, state->arena, index, token, state->contents); - } - else if (token->kind == TokenBaseKind_Whitespace){ - Range_i64 range = Ii64(token); - u8 *ptr = state->contents.str + range.one_past_last - 1; - for (i64 i = range.one_past_last - 1; - i >= range.first; - i -= 1, ptr -= 1){ - if (*ptr == '\n'){ - state->prev_line_start = ptr + 1; - break; - } - } - } - else{ - break; - } - generic_parse_inc(state); - token = token_it_read(&state->it); - } -} - -function void -generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_Comment_Function *handle_comment, Generic_Parse_State *state){ - state->app = app; - state->arena = arena; - state->contents = contents; - state->it = token_iterator(0, tokens); - state->handle_comment = handle_comment; - state->prev_line_start = contents.str; -} - -//////////////////////////////// - -#if 0 -// NOTE(allen): grammar syntax -(X) = X -X Y = X and then Y -X? = zero or one X -$X = check for X but don't consume -[X] = zero or more Xs -X | Y = either X or Y -* = anything that does not match previous options in a X | Y | ... chain -* - X = anything that does not match X or previous options in a Y | Z | ... chain - = a token of type X -"X" = literally the string "X" -X{Y} = X with flag Y - -// NOTE(allen): grammar of code index parse -file: [preprocessor | scope | parens | function | type | * - ] -preprocessor: [scope | parens | stmnt]{pp-body} -scope: [preprocessor | scope | parens | * - ] -paren: [preprocessor | scope | parens | * - ] -stmnt-close-pattern: | | | | | -stmnt: [type | * - stmnt-close-pattern] stmnt-close-pattern -type: struct | union | enum | typedef -struct: "struct" $(";" | "{") -union: "union" $(";" | "{") -enum: "enum" $(";" | "{") -typedef: "typedef" [* - ( (";" | "("))] $(";" | "(") -function: >"(" [* - ("(" | ")" | "{" | "}" | ";")] ")" ("{" | ";") - -#endif - -//////////////////////////////// - -function Code_Index_Note* -index_new_note(Code_Index_File *index, Generic_Parse_State *state, Range_i64 range, Code_Index_Note_Kind kind, Code_Index_Nest *parent){ - Code_Index_Note *result = push_array(state->arena, Code_Index_Note, 1); - sll_queue_push(index->note_list.first, index->note_list.last, result); - index->note_list.count += 1; - result->note_kind = kind; - result->pos = range; - result->text = push_string_copy(state->arena, string_substring(state->contents, range)); - result->file = index; - result->parent = parent; - return(result); -} - -function void -cpp_parse_type_structure(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - if (state->finished){ - return; - } - Token *token = token_it_read(&state->it); - if (token != 0 && token->kind == TokenBaseKind_Identifier){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - Token *peek = token_it_read(&state->it); - if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || - peek->kind == TokenBaseKind_ScopeOpen){ - index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); - } - } -} - -function void -cpp_parse_type_def(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - for (;;){ - b32 did_advance = false; - Token *token = token_it_read(&state->it); - if (token == 0 || state->finished){ - break; - } - if (token->kind == TokenBaseKind_Identifier){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - did_advance = true; - Token *peek = token_it_read(&state->it); - if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || - peek->kind == TokenBaseKind_ParentheticalOpen){ - index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); - break; - } - } - else if (token->kind == TokenBaseKind_StatementClose || - token->kind == TokenBaseKind_ScopeOpen || - token->kind == TokenBaseKind_ScopeClose || - token->kind == TokenBaseKind_ScopeOpen || - token->kind == TokenBaseKind_ScopeClose){ - break; - } - else if (token->kind == TokenBaseKind_Keyword){ - String_Const_u8 lexeme = string_substring(state->contents, Ii64(token)); - if (string_match(lexeme, string_u8_litexpr("struct")) || - string_match(lexeme, string_u8_litexpr("union")) || - string_match(lexeme, string_u8_litexpr("enum"))){ - break; - } - } - if (!did_advance){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - } - } -} - -function void -cpp_parse_function(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ - Token *token = token_it_read(&state->it); - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - if (state->finished){ - return; - } - Token *peek = token_it_read(&state->it); - Token *reset_point = peek; - if (peek != 0 && peek->sub_kind == TokenCppKind_ParenOp){ - b32 at_paren_close = false; - for (; peek != 0;){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - peek = token_it_read(&state->it); - if (peek == 0 || state->finished){ - break; - } - - if (peek->kind == TokenBaseKind_ParentheticalOpen || - peek->kind == TokenBaseKind_ScopeOpen || - peek->kind == TokenBaseKind_ScopeClose || - peek->kind == TokenBaseKind_StatementClose){ - break; - } - if (peek->kind == TokenBaseKind_ParentheticalClose){ - at_paren_close = true; - break; - } - } - - if (at_paren_close){ - generic_parse_inc(state); - generic_parse_skip_soft_tokens(index, state); - peek = token_it_read(&state->it); - if (peek != 0 && - peek->kind == TokenBaseKind_ScopeOpen || - peek->kind == TokenBaseKind_StatementClose){ - index_new_note(index, state, Ii64(token), CodeIndexNote_Function, parent); - } - } - } - state->it = token_iterator(state->it.user_id, state->it.tokens, state->it.count, reset_point); -} - -function Code_Index_Nest* -generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state); - -function Code_Index_Nest* -generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state); - -function Code_Index_Nest* -generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state); - -function Code_Index_Nest* -generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state); - -function Code_Index_Nest* -generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state){ - Token *token = token_it_read(&state->it); - Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); - result->kind = CodeIndexNest_Statement; - result->open = Ii64(token->pos); - result->close = Ii64(max_i64); - result->file = index; - - state->in_statement = true; - - for (;;){ - generic_parse_skip_soft_tokens(index, state); - token = token_it_read(&state->it); - if (token == 0 || state->finished){ - break; - } - - if (state->in_preprocessor){ - if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || - token->kind == TokenBaseKind_Preprocessor){ - result->is_closed = true; - result->close = Ii64(token->pos); - break; - } - } - else{ - if (token->kind == TokenBaseKind_Preprocessor){ - result->is_closed = true; - result->close = Ii64(token->pos); - break; - } - } - - if (token->kind == TokenBaseKind_ScopeOpen || - token->kind == TokenBaseKind_ScopeClose || - token->kind == TokenBaseKind_ParentheticalOpen){ - result->is_closed = true; - result->close = Ii64(token->pos); - break; - } - - if (token->kind == TokenBaseKind_StatementClose){ - result->is_closed = true; - result->close = Ii64(token); - generic_parse_inc(state); - break; - } - - generic_parse_inc(state); - } - - state->in_statement = false; - - return(result); -} - -function Code_Index_Nest* -generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state){ - Token *token = token_it_read(&state->it); - Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); - result->kind = CodeIndexNest_Preprocessor; - result->open = Ii64(token->pos); - result->close = Ii64(max_i64); - result->file = index; - - state->in_preprocessor = true; - - b32 potential_macro = false; - if (state->do_cpp_parse){ - if (token->sub_kind == TokenCppKind_PPDefine){ - potential_macro = true; - } - } - - generic_parse_inc(state); - for (;;){ - generic_parse_skip_soft_tokens(index, state); - token = token_it_read(&state->it); - if (token == 0 || state->finished){ - break; - } - - if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || - token->kind == TokenBaseKind_Preprocessor){ - result->is_closed = true; - result->close = Ii64(token->pos); - break; - } - - if (state->do_cpp_parse && potential_macro){ - if (token->sub_kind == TokenCppKind_Identifier){ - index_new_note(index, state, Ii64(token), CodeIndexNote_Macro, result); - } - potential_macro = false; - } - - if (token->kind == TokenBaseKind_ScopeOpen){ - Code_Index_Nest *nest = generic_parse_scope(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - if (token->kind == TokenBaseKind_ParentheticalOpen){ - Code_Index_Nest *nest = generic_parse_paren(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - generic_parse_inc(state); - } - - result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); - - state->in_preprocessor = false; - - return(result); -} - -function Code_Index_Nest* -generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state){ - Token *token = token_it_read(&state->it); - Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); - result->kind = CodeIndexNest_Scope; - result->open = Ii64(token); - result->close = Ii64(max_i64); - result->file = index; - - state->scope_counter += 1; - - generic_parse_inc(state); - for (;;){ - generic_parse_skip_soft_tokens(index, state); - token = token_it_read(&state->it); - if (token == 0 || state->finished){ - break; - } - - if (state->in_preprocessor){ - if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || - token->kind == TokenBaseKind_Preprocessor){ - break; - } - } - else{ - if (token->kind == TokenBaseKind_Preprocessor){ - Code_Index_Nest *nest = generic_parse_preprocessor(index, state); - code_index_push_nest(&index->nest_list, nest); - continue; - } - } - - if (token->kind == TokenBaseKind_ScopeClose){ - result->is_closed = true; - result->close = Ii64(token); - generic_parse_inc(state); - break; - } - - if (token->kind == TokenBaseKind_ScopeOpen){ - Code_Index_Nest *nest = generic_parse_scope(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - if (token->kind == TokenBaseKind_ParentheticalOpen){ - Code_Index_Nest *nest = generic_parse_paren(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - if (token->kind == TokenBaseKind_ParentheticalClose){ - generic_parse_inc(state); - continue; - } - - { - Code_Index_Nest *nest = generic_parse_statement(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - } - } - - result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); - - state->scope_counter -= 1; - - return(result); -} - -function Code_Index_Nest* -generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state){ - Token *token = token_it_read(&state->it); - Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); - result->kind = CodeIndexNest_Paren; - result->open = Ii64(token); - result->close = Ii64(max_i64); - result->file = index; - - i64 manifested_characters_on_line = 0; - { - u8 *ptr = state->prev_line_start; - u8 *end_ptr = state->contents.str + token->pos; - // NOTE(allen): Initial whitespace - for (;ptr < end_ptr; ptr += 1){ - if (!character_is_whitespace(*ptr)){ - break; - } - } - // NOTE(allen): Manifested characters - manifested_characters_on_line = (i64)(end_ptr - ptr) + token->size; - } - - state->paren_counter += 1; - - generic_parse_inc(state); - for (;;){ - generic_parse_skip_soft_tokens(index, state); - token = token_it_read(&state->it); - if (token == 0 || state->finished){ - break; - } - - if (state->in_preprocessor){ - if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || - token->kind == TokenBaseKind_Preprocessor){ - break; - } - } - else{ - if (token->kind == TokenBaseKind_Preprocessor){ - Code_Index_Nest *nest = generic_parse_preprocessor(index, state); - code_index_push_nest(&index->nest_list, nest); - continue; - } - } - - if (token->kind == TokenBaseKind_ParentheticalClose){ - result->is_closed = true; - result->close = Ii64(token); - generic_parse_inc(state); - break; - } - - if (token->kind == TokenBaseKind_ScopeClose){ - break; - } - - if (token->kind == TokenBaseKind_ScopeOpen){ - Code_Index_Nest *nest = generic_parse_scope(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - if (token->kind == TokenBaseKind_ParentheticalOpen){ - Code_Index_Nest *nest = generic_parse_paren(index, state); - nest->parent = result; - code_index_push_nest(&result->nest_list, nest); - continue; - } - - generic_parse_inc(state); - } - - result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); - - state->paren_counter -= 1; - - return(result); -} - -function b32 -generic_parse_full_input_breaks(Code_Index_File *index, Generic_Parse_State *state, i32 limit){ - b32 result = false; - - i64 first_index = token_it_index(&state->it); - i64 one_past_last_index = first_index + limit; - for (;;){ - generic_parse_skip_soft_tokens(index, state); - Token *token = token_it_read(&state->it); - - if (token == 0 || state->finished){ - result = true; - break; - } - - if (token->kind == TokenBaseKind_Preprocessor){ - Code_Index_Nest *nest = generic_parse_preprocessor(index, state); - code_index_push_nest(&index->nest_list, nest); - } - else if (token->kind == TokenBaseKind_ScopeOpen){ - Code_Index_Nest *nest = generic_parse_scope(index, state); - code_index_push_nest(&index->nest_list, nest); - } - else if (token->kind == TokenBaseKind_ParentheticalOpen){ - Code_Index_Nest *nest = generic_parse_paren(index, state); - code_index_push_nest(&index->nest_list, nest); - } - else if (state->do_cpp_parse){ - if (token->sub_kind == TokenCppKind_Struct || - token->sub_kind == TokenCppKind_Union || - token->sub_kind == TokenCppKind_Enum){ - cpp_parse_type_structure(index, state, 0); - } - else if (token->sub_kind == TokenCppKind_Typedef){ - cpp_parse_type_def(index, state, 0); - } - else if (token->sub_kind == TokenCppKind_Identifier){ - cpp_parse_function(index, state, 0); - } - else{ - generic_parse_inc(state); - } - } - else{ - generic_parse_inc(state); - } - - i64 index = token_it_index(&state->it); - if (index >= one_past_last_index){ - token = token_it_read(&state->it); - if (token == 0){ - result = true; - } - break; - } - } - - if (result){ - index->nest_array = code_index_nest_ptr_array_from_list(state->arena, &index->nest_list); - index->note_array = code_index_note_ptr_array_from_list(state->arena, &index->note_list); - } - - return(result); -} - -//////////////////////////////// - -function void -default_comment_index(Application_Links *app, Arena *arena, Code_Index_File *index, Token *token, String_Const_u8 contents){ - -} - -function void -generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_State *state){ - generic_parse_init(app, arena, contents, tokens, default_comment_index, state); -} - -//////////////////////////////// - -function Token_Pair -layout_token_pair(Token_Array *tokens, i64 pos){ - Token_Pair result = {}; - Token_Iterator_Array it = token_iterator_pos(0, tokens, pos); - Token *b = token_it_read(&it); - if (b != 0){ - if (b->kind == TokenBaseKind_Whitespace){ - token_it_inc_non_whitespace(&it); - b = token_it_read(&it); - } - } - token_it_dec_non_whitespace(&it); - Token *a = token_it_read(&it); - if (a != 0){ - result.a = *a; - } - if (b != 0){ - result.b = *b; - } - return(result); -} - -function f32 -layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 space_advance, b32 *unresolved_dependence){ - f32 result = 0.f; - if (nest != 0){ - switch (nest->kind){ - case CodeIndexNest_Scope: - case CodeIndexNest_Preprocessor: - { - result = layout_index_x_shift(app, reflex, nest->parent, pos, space_advance, unresolved_dependence); - if (nest->open.min < pos && nest->open.max <= pos && - (!nest->is_closed || pos < nest->close.min)){ - result += 4.f*space_advance; - } - }break; - - case CodeIndexNest_Statement: - { - result = layout_index_x_shift(app, reflex, nest->parent, pos, space_advance, unresolved_dependence); - if (nest->open.min < pos && nest->open.max <= pos && - (!nest->is_closed || pos < nest->close.min)){ - result += 4.f*space_advance; - } - }break; - - case CodeIndexNest_Paren: - { - Rect_f32 box = layout_reflex_get_rect(app, reflex, nest->open.max - 1, unresolved_dependence); - result = box.x1; - }break; - } - } - return(result); -} - -function f32 -layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 space_advance){ - b32 ignore; - return(layout_index_x_shift(app, reflex, nest, pos, space_advance, &ignore)); -} - -function f32 -layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 space_advance, b32 *unresolved_dependence){ - f32 indent = 0; - Code_Index_Nest *nest = code_index_get_nest(file, pos); - if (nest != 0){ - indent = layout_index_x_shift(app, reflex, nest, pos, space_advance, unresolved_dependence); - } - return(indent); -} - -function f32 -layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 space_advance){ - b32 ignore; - return(layout_index_x_shift(app, reflex, file, pos, space_advance, &ignore)); -} - -function void -layout_index__emit_chunk(LefRig_TopBot_Layout_Vars *pos_vars, Face_ID face, Arena *arena, u8 *text_str, i64 range_first, u8 *ptr, u8 *end, Layout_Item_List *list){ - for (;ptr < end;){ - Character_Consume_Result consume = utf8_consume(ptr, (u64)(end - ptr)); - if (consume.codepoint != '\r'){ - i64 index = layout_index_from_ptr(ptr, text_str, range_first); - if (consume.codepoint != max_u32){ - lr_tb_write(pos_vars, face, arena, list, index, consume.codepoint); - } - else{ - lr_tb_write_byte(pos_vars, face, arena, list, index, *ptr); - } - } - ptr += consume.inc; - } -} - -function i32 -layout_token_score_wrap_token(Token_Pair *pair, Token_Cpp_Kind kind){ - i32 result = 0; - if (pair->a.sub_kind != kind && pair->b.sub_kind == kind){ - result -= 1; - } - else if (pair->a.sub_kind == kind && pair->b.sub_kind != kind){ - result += 1; - } - return(result); -} - -function Layout_Item_List -layout_index__inner(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Code_Index_File *file, Layout_Wrap_Kind kind){ - Scratch_Block scratch(app); - - Managed_Scope scope = buffer_get_managed_scope(app, buffer); - Token_Array *tokens_ptr = scope_attachment(app, scope, attachment_tokens, Token_Array); - - Layout_Item_List list = get_empty_item_list(range); - String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); - - Face_Advance_Map advance_map = get_face_advance_map(app, face); - Face_Metrics metrics = get_face_metrics(app, face); - LefRig_TopBot_Layout_Vars pos_vars = get_lr_tb_layout_vars(&advance_map, &metrics, width); - - f32 wrap_align_x = width - metrics.normal_advance; - - Layout_Reflex reflex = get_layout_reflex(&list, buffer, width, face); - - if (text.size == 0){ - lr_tb_write_blank(&pos_vars, face, arena, &list, range.start); - } - else{ - b32 first_of_the_line = true; - Newline_Layout_Vars newline_vars = get_newline_layout_vars(); - - u8 *ptr = text.str; - u8 *end_ptr = ptr + text.size; - u8 *word_ptr = ptr; - - u8 *pending_wrap_ptr = ptr; - f32 pending_wrap_x = 0.f; - i32 pending_wrap_paren_nest_count = 0; - i32 pending_wrap_token_score = 0; - f32 pending_wrap_accumulated_w = 0.f; - - start: - if (ptr == end_ptr){ - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); - goto finish; - } - - if (!character_is_whitespace(*ptr)){ - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); - goto consuming_non_whitespace; - } - - { - for (;ptr < end_ptr; ptr += 1){ - if (!character_is_whitespace(*ptr)){ - pending_wrap_ptr = ptr; - word_ptr = ptr; - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); - goto consuming_non_whitespace; - } - if (*ptr == '\n'){ - pending_wrap_ptr = ptr; - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); - goto consuming_normal_whitespace; - } - } - - if (ptr == end_ptr){ - pending_wrap_ptr = ptr; - i64 index = layout_index_from_ptr(ptr - 1, text.str, range.first); - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); - goto finish; - } - } - - consuming_non_whitespace: - { - for (;ptr <= end_ptr; ptr += 1){ - if (ptr == end_ptr || character_is_whitespace(*ptr)){ - break; - } - } - - // NOTE(allen): measure this word - newline_layout_consume_default(&newline_vars); - String_Const_u8 word = SCu8(word_ptr, ptr); - u8 *word_end = ptr; - { - f32 word_advance = 0.f; - ptr = word.str; - for (;ptr < word_end;){ - Character_Consume_Result consume = utf8_consume(ptr, (u64)(word_end - ptr)); - if (consume.codepoint != max_u32){ - word_advance += lr_tb_advance(&pos_vars, face, consume.codepoint); - } - else{ - word_advance += lr_tb_advance_byte(&pos_vars); - } - ptr += consume.inc; - } - pending_wrap_accumulated_w += word_advance; - } - - if (!first_of_the_line && (kind == Layout_Wrapped) && lr_tb_crosses_width(&pos_vars, pending_wrap_accumulated_w)){ - i64 index = layout_index_from_ptr(pending_wrap_ptr, text.str, range.first); - lr_tb_align_rightward(&pos_vars, wrap_align_x); - lr_tb_write_ghost(&pos_vars, face, arena, &list, index, '\\'); - - lr_tb_next_line(&pos_vars); -#if 0 - f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); - lr_tb_advance_x_without_item(&pos_vars, shift); -#endif - - ptr = pending_wrap_ptr; - pending_wrap_accumulated_w = 0.f; - first_of_the_line = true; - goto start; - } - } - - consuming_normal_whitespace: - for (; ptr < end_ptr; ptr += 1){ - if (!character_is_whitespace(*ptr)){ - u8 *new_wrap_ptr = ptr; - - i64 index = layout_index_from_ptr(new_wrap_ptr, text.str, range.first); - Code_Index_Nest *new_wrap_nest = code_index_get_nest(file, index); - b32 invalid_wrap_x = false; - f32 new_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, metrics.space_advance, &invalid_wrap_x); - if (invalid_wrap_x){ - new_wrap_x = max_f32; - } - - i32 new_wrap_paren_nest_count = 0; - for (Code_Index_Nest *nest = new_wrap_nest; - nest != 0; - nest = nest->parent){ - if (nest->kind == CodeIndexNest_Paren){ - new_wrap_paren_nest_count += 1; - } - } - - Token_Pair new_wrap_token_pair = layout_token_pair(tokens_ptr, index); - - // TODO(allen): pull out the token scoring part and make it replacable for other - // language's token based wrap scoring needs. - i32 token_score = 0; - if (new_wrap_token_pair.a.kind == TokenBaseKind_Keyword){ - if (new_wrap_token_pair.b.kind == TokenBaseKind_ParentheticalOpen || - new_wrap_token_pair.b.kind == TokenBaseKind_Keyword){ - token_score -= 2; - } - } - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Eq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_PlusEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_MinusEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_StarEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_DivEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_ModEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_LeftLeftEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_RightRightEq); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Comma); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_AndAnd); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_OrOr); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Ternary); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Colon); - token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Semicolon); - - i32 new_wrap_token_score = token_score; - - b32 new_wrap_ptr_is_better = false; - if (first_of_the_line){ - new_wrap_ptr_is_better = true; - } - else{ - if (new_wrap_token_score > pending_wrap_token_score){ - new_wrap_ptr_is_better = true; - } - else if (new_wrap_token_score == pending_wrap_token_score){ - f32 new_score = new_wrap_paren_nest_count*10.f + new_wrap_x; - f32 old_score = pending_wrap_paren_nest_count*10.f + pending_wrap_x + metrics.normal_advance*4.f + pending_wrap_accumulated_w*0.5f; - - if (new_score < old_score){ - new_wrap_ptr_is_better = true; - } - } - } - - if (new_wrap_ptr_is_better){ - layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, new_wrap_ptr, &list); - first_of_the_line = false; - - pending_wrap_ptr = new_wrap_ptr; - pending_wrap_paren_nest_count = new_wrap_paren_nest_count; - pending_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, metrics.space_advance); - pending_wrap_paren_nest_count = new_wrap_paren_nest_count; - pending_wrap_token_score = new_wrap_token_score; - pending_wrap_accumulated_w = 0.f; - } - - word_ptr = ptr; - goto consuming_non_whitespace; - } - - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - switch (*ptr){ - default: - { - newline_layout_consume_default(&newline_vars); - pending_wrap_accumulated_w += lr_tb_advance(&pos_vars, face, *ptr); - }break; - - case '\r': - { - newline_layout_consume_CR(&newline_vars, index); - }break; - - case '\n': - { - layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); - pending_wrap_ptr = ptr + 1; - pending_wrap_accumulated_w = 0.f; - - u64 newline_index = newline_layout_consume_LF(&newline_vars, index); - lr_tb_write_blank(&pos_vars, face, arena, &list, newline_index); - lr_tb_next_line(&pos_vars); - first_of_the_line = true; - ptr += 1; - goto start; - }break; - } - } - - finish: - if (newline_layout_consume_finish(&newline_vars)){ - layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); - i64 index = layout_index_from_ptr(ptr, text.str, range.first); - lr_tb_write_blank(&pos_vars, face, arena, &list, index); - } - } - - layout_item_list_finish(&list, -pos_vars.line_to_text_shift); - - return(list); -} - -function Layout_Item_List -layout_virt_indent_index(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Layout_Wrap_Kind kind){ - Layout_Item_List result = {}; - - if (global_config.enable_virtual_whitespace){ - code_index_lock(); - Code_Index_File *file = code_index_get_file(buffer); - if (file != 0){ - result = layout_index__inner(app, arena, buffer, range, face, width, file, kind); - } - code_index_unlock(); - if (file == 0){ - result = layout_virt_indent_literal(app, arena, buffer, range, face, width, kind); - } - } - else{ - result = layout_basic(app, arena, buffer, range, face, width, kind); - } - - return(result); -} - -function Layout_Item_List -layout_virt_indent_index_unwrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ - return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Unwrapped)); -} - -function Layout_Item_List -layout_virt_indent_index_wrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ - return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Wrapped)); -} - -function Layout_Item_List -layout_virt_indent_index_generic(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ - Managed_Scope scope = buffer_get_managed_scope(app, buffer); - b32 *wrap_lines_ptr = scope_attachment(app, scope, buffer_wrap_lines, b32); - b32 wrap_lines = (wrap_lines_ptr != 0 && *wrap_lines_ptr); - return(layout_virt_indent_index(app, arena, buffer, range, face, width, wrap_lines?Layout_Wrapped:Layout_Unwrapped)); -} - -CUSTOM_COMMAND_SIG(toggle_virtual_whitespace) -CUSTOM_DOC("Toggles the current buffer's virtual whitespace status.") -{ - global_config.enable_virtual_whitespace = !global_config.enable_virtual_whitespace; - - for (Buffer_ID buffer = get_buffer_next(app, 0, Access_Always); - buffer != 0; - buffer = get_buffer_next(app, buffer, Access_Always)){ - buffer_clear_layout_cache(app, buffer); - } -} - -// BOTTOM - +/* +4coder_code_index.cpp - Generic code indexing system for layout, definition jumps, etc. +*/ + +// TOP + +global Code_Index global_code_index = {}; + +function void +code_index_init(void){ + global_code_index.mutex = system_mutex_make(); + global_code_index.node_arena = make_arena_system(KB(4)); + global_code_index.buffer_to_index_file = make_table_u64_u64(global_code_index.node_arena.base_allocator, 500); +} + +function Code_Index_File_Storage* +code_index__alloc_storage(void){ + Code_Index_File_Storage *result = global_code_index.free_storage; + if (result == 0){ + result = push_array_zero(&global_code_index.node_arena, Code_Index_File_Storage, 1); + } + else{ + sll_stack_pop(global_code_index.free_storage); + } + zdll_push_back(global_code_index.storage_first, global_code_index.storage_last, result); + global_code_index.storage_count += 1; + return(result); +} + +function void +code_index__free_storage(Code_Index_File_Storage *storage){ + zdll_remove(global_code_index.storage_first, global_code_index.storage_last, storage); + global_code_index.storage_count -= 1; + sll_stack_push(global_code_index.free_storage, storage); +} + +function void +code_index_push_nest(Code_Index_Nest_List *list, Code_Index_Nest *nest){ + sll_queue_push(list->first, list->last, nest); + list->count += 1; +} + +function Code_Index_Nest_Ptr_Array +code_index_nest_ptr_array_from_list(Arena *arena, Code_Index_Nest_List *list){ + Code_Index_Nest_Ptr_Array array = {}; + array.ptrs = push_array_zero(arena, Code_Index_Nest*, list->count); + array.count = list->count; + i32 counter = 0; + for (Code_Index_Nest *node = list->first; + node != 0; + node = node->next){ + array.ptrs[counter] = node; + counter += 1; + } + return(array); +} + +function Code_Index_Note_Ptr_Array +code_index_note_ptr_array_from_list(Arena *arena, Code_Index_Note_List *list){ + Code_Index_Note_Ptr_Array array = {}; + array.ptrs = push_array_zero(arena, Code_Index_Note*, list->count); + array.count = list->count; + i32 counter = 0; + for (Code_Index_Note *node = list->first; + node != 0; + node = node->next){ + array.ptrs[counter] = node; + counter += 1; + } + return(array); +} + +function void +code_index_lock(void){ + system_mutex_acquire(global_code_index.mutex); +} + +function void +code_index_unlock(void){ + system_mutex_release(global_code_index.mutex); +} + +function void +code_index_set_file(Buffer_ID buffer, Arena arena, Code_Index_File *index){ + Code_Index_File_Storage *storage = 0; + Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); + if (lookup.found_match){ + u64 val = 0; + table_read(&global_code_index.buffer_to_index_file, lookup, &val); + storage = (Code_Index_File_Storage*)IntAsPtr(val); + linalloc_clear(&storage->arena); + } + else{ + storage = code_index__alloc_storage(); + table_insert(&global_code_index.buffer_to_index_file, buffer, (u64)PtrAsInt(storage)); + } + storage->arena = arena; + storage->file = index; +} + +function void +code_index_erase_file(Buffer_ID buffer){ + Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); + if (lookup.found_match){ + u64 val = 0; + table_read(&global_code_index.buffer_to_index_file, lookup, &val); + Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); + linalloc_clear(&storage->arena); + table_erase(&global_code_index.buffer_to_index_file, lookup); + code_index__free_storage(storage); + } +} + +function Code_Index_File* +code_index_get_file(Buffer_ID buffer){ + Code_Index_File *result = 0; + Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); + if (lookup.found_match){ + u64 val = 0; + table_read(&global_code_index.buffer_to_index_file, lookup, &val); + Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); + result = storage->file; + } + return(result); +} + +function Code_Index_Nest* +code_index_get_nest(Code_Index_Nest_Ptr_Array *array, i64 pos){ + Code_Index_Nest *result = 0; + i32 count = array->count; + Code_Index_Nest **nest_ptrs = array->ptrs; + for (i32 i = 0; i < count; i += 1){ + Code_Index_Nest *nest = nest_ptrs[i]; + if (nest->open.max <= pos && pos <= nest->close.min){ + Code_Index_Nest *sub_nest = + code_index_get_nest(&nest->nest_array, pos); + if (sub_nest != 0){ + result = sub_nest; + } + else{ + result = nest; + } + break; + } + } + return(result); +} + +function Code_Index_Nest* +code_index_get_nest(Code_Index_Nest *nest, i64 pos){ + return(code_index_get_nest(&nest->nest_array, pos)); +} + +function Code_Index_Nest* +code_index_get_nest(Code_Index_File *file, i64 pos){ + return(code_index_get_nest(&file->nest_array, pos)); +} + +function void +index_shift(i64 *ptr, Range_i64 old_range, u64 new_size){ + i64 i = *ptr; + if (old_range.min <= i && i < old_range.max){ + *ptr = old_range.first; + } + else if (old_range.max <= i){ + *ptr = i + new_size - (old_range.max - old_range.min); + } +} + +function void +code_index_shift(Code_Index_Nest_Ptr_Array *array, + Range_i64 old_range, u64 new_size){ + i32 count = array->count; + Code_Index_Nest **nest_ptr = array->ptrs; + for (i32 i = 0; i < count; i += 1, nest_ptr += 1){ + Code_Index_Nest *nest = *nest_ptr; + index_shift(&nest->open.min, old_range, new_size); + index_shift(&nest->open.max, old_range, new_size); + if (nest->is_closed){ + index_shift(&nest->close.min, old_range, new_size); + index_shift(&nest->close.max, old_range, new_size); + } + code_index_shift(&nest->nest_array, old_range, new_size); + } +} + +function void +code_index_shift(Code_Index_File *file, Range_i64 old_range, u64 new_size){ + code_index_shift(&file->nest_array, old_range, new_size); +} + +//////////////////////////////// + +function void +generic_parse_inc(Generic_Parse_State *state){ + if (!token_it_inc_all(&state->it)){ + state->finished = true; + } +} + +function void +generic_parse_skip_soft_tokens(Code_Index_File *index, Generic_Parse_State *state){ + Token *token = token_it_read(&state->it); + for (;token != 0 && !state->finished;){ + if (state->in_preprocessor && !HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){ + break; + } + if (token->kind == TokenBaseKind_Comment){ + state->handle_comment(state->app, state->arena, index, token, state->contents); + } + else if (token->kind == TokenBaseKind_Whitespace){ + Range_i64 range = Ii64(token); + u8 *ptr = state->contents.str + range.one_past_last - 1; + for (i64 i = range.one_past_last - 1; + i >= range.first; + i -= 1, ptr -= 1){ + if (*ptr == '\n'){ + state->prev_line_start = ptr + 1; + break; + } + } + } + else{ + break; + } + generic_parse_inc(state); + token = token_it_read(&state->it); + } +} + +function void +generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_Comment_Function *handle_comment, Generic_Parse_State *state){ + state->app = app; + state->arena = arena; + state->contents = contents; + state->it = token_iterator(0, tokens); + state->handle_comment = handle_comment; + state->prev_line_start = contents.str; +} + +//////////////////////////////// + +#if 0 +// NOTE(allen): grammar syntax +(X) = X +X Y = X and then Y +X? = zero or one X +$X = check for X but dont consume // NOTE(yuval): Removed apostrophe as it was causing a warning when compiling with gcc +[X] = zero or more Xs +X | Y = either X or Y +* = anything that does not match previous options in a X | Y | ... chain +* - X = anything that does not match X or previous options in a Y | Z | ... chain + = a token of type X +"X" = literally the string "X" +X{Y} = X with flag Y + +// NOTE(allen): grammar of code index parse +file: [preprocessor | scope | parens | function | type | * - ] +preprocessor: [scope | parens | stmnt]{pp-body} +scope: [preprocessor | scope | parens | * - ] +paren: [preprocessor | scope | parens | * - ] +stmnt-close-pattern: | | | | | +stmnt: [type | * - stmnt-close-pattern] stmnt-close-pattern +type: struct | union | enum | typedef +struct: "struct" $(";" | "{") +union: "union" $(";" | "{") +enum: "enum" $(";" | "{") +typedef: "typedef" [* - ( (";" | "("))] $(";" | "(") +function: >"(" [* - ("(" | ")" | "{" | "}" | ";")] ")" ("{" | ";") + +#endif + +//////////////////////////////// + +function Code_Index_Note* +index_new_note(Code_Index_File *index, Generic_Parse_State *state, Range_i64 range, Code_Index_Note_Kind kind, Code_Index_Nest *parent){ + Code_Index_Note *result = push_array(state->arena, Code_Index_Note, 1); + sll_queue_push(index->note_list.first, index->note_list.last, result); + index->note_list.count += 1; + result->note_kind = kind; + result->pos = range; + result->text = push_string_copy(state->arena, string_substring(state->contents, range)); + result->file = index; + result->parent = parent; + return(result); +} + +function void +cpp_parse_type_structure(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + if (state->finished){ + return; + } + Token *token = token_it_read(&state->it); + if (token != 0 && token->kind == TokenBaseKind_Identifier){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + Token *peek = token_it_read(&state->it); + if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || + peek->kind == TokenBaseKind_ScopeOpen){ + index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); + } + } +} + +function void +cpp_parse_type_def(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + for (;;){ + b32 did_advance = false; + Token *token = token_it_read(&state->it); + if (token == 0 || state->finished){ + break; + } + if (token->kind == TokenBaseKind_Identifier){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + did_advance = true; + Token *peek = token_it_read(&state->it); + if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || + peek->kind == TokenBaseKind_ParentheticalOpen){ + index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); + break; + } + } + else if (token->kind == TokenBaseKind_StatementClose || + token->kind == TokenBaseKind_ScopeOpen || + token->kind == TokenBaseKind_ScopeClose || + token->kind == TokenBaseKind_ScopeOpen || + token->kind == TokenBaseKind_ScopeClose){ + break; + } + else if (token->kind == TokenBaseKind_Keyword){ + String_Const_u8 lexeme = string_substring(state->contents, Ii64(token)); + if (string_match(lexeme, string_u8_litexpr("struct")) || + string_match(lexeme, string_u8_litexpr("union")) || + string_match(lexeme, string_u8_litexpr("enum"))){ + break; + } + } + if (!did_advance){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + } + } +} + +function void +cpp_parse_function(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ + Token *token = token_it_read(&state->it); + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + if (state->finished){ + return; + } + Token *peek = token_it_read(&state->it); + Token *reset_point = peek; + if (peek != 0 && peek->sub_kind == TokenCppKind_ParenOp){ + b32 at_paren_close = false; + for (; peek != 0;){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + peek = token_it_read(&state->it); + if (peek == 0 || state->finished){ + break; + } + + if (peek->kind == TokenBaseKind_ParentheticalOpen || + peek->kind == TokenBaseKind_ScopeOpen || + peek->kind == TokenBaseKind_ScopeClose || + peek->kind == TokenBaseKind_StatementClose){ + break; + } + if (peek->kind == TokenBaseKind_ParentheticalClose){ + at_paren_close = true; + break; + } + } + + if (at_paren_close){ + generic_parse_inc(state); + generic_parse_skip_soft_tokens(index, state); + peek = token_it_read(&state->it); + if (peek != 0 && + peek->kind == TokenBaseKind_ScopeOpen || + peek->kind == TokenBaseKind_StatementClose){ + index_new_note(index, state, Ii64(token), CodeIndexNote_Function, parent); + } + } + } + state->it = token_iterator(state->it.user_id, state->it.tokens, state->it.count, reset_point); +} + +function Code_Index_Nest* +generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state); + +function Code_Index_Nest* +generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state); + +function Code_Index_Nest* +generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state); + +function Code_Index_Nest* +generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state); + +function Code_Index_Nest* +generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state){ + Token *token = token_it_read(&state->it); + Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); + result->kind = CodeIndexNest_Statement; + result->open = Ii64(token->pos); + result->close = Ii64(max_i64); + result->file = index; + + state->in_statement = true; + + for (;;){ + generic_parse_skip_soft_tokens(index, state); + token = token_it_read(&state->it); + if (token == 0 || state->finished){ + break; + } + + if (state->in_preprocessor){ + if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || + token->kind == TokenBaseKind_Preprocessor){ + result->is_closed = true; + result->close = Ii64(token->pos); + break; + } + } + else{ + if (token->kind == TokenBaseKind_Preprocessor){ + result->is_closed = true; + result->close = Ii64(token->pos); + break; + } + } + + if (token->kind == TokenBaseKind_ScopeOpen || + token->kind == TokenBaseKind_ScopeClose || + token->kind == TokenBaseKind_ParentheticalOpen){ + result->is_closed = true; + result->close = Ii64(token->pos); + break; + } + + if (token->kind == TokenBaseKind_StatementClose){ + result->is_closed = true; + result->close = Ii64(token); + generic_parse_inc(state); + break; + } + + generic_parse_inc(state); + } + + state->in_statement = false; + + return(result); +} + +function Code_Index_Nest* +generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state){ + Token *token = token_it_read(&state->it); + Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); + result->kind = CodeIndexNest_Preprocessor; + result->open = Ii64(token->pos); + result->close = Ii64(max_i64); + result->file = index; + + state->in_preprocessor = true; + + b32 potential_macro = false; + if (state->do_cpp_parse){ + if (token->sub_kind == TokenCppKind_PPDefine){ + potential_macro = true; + } + } + + generic_parse_inc(state); + for (;;){ + generic_parse_skip_soft_tokens(index, state); + token = token_it_read(&state->it); + if (token == 0 || state->finished){ + break; + } + + if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || + token->kind == TokenBaseKind_Preprocessor){ + result->is_closed = true; + result->close = Ii64(token->pos); + break; + } + + if (state->do_cpp_parse && potential_macro){ + if (token->sub_kind == TokenCppKind_Identifier){ + index_new_note(index, state, Ii64(token), CodeIndexNote_Macro, result); + } + potential_macro = false; + } + + if (token->kind == TokenBaseKind_ScopeOpen){ + Code_Index_Nest *nest = generic_parse_scope(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + if (token->kind == TokenBaseKind_ParentheticalOpen){ + Code_Index_Nest *nest = generic_parse_paren(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + generic_parse_inc(state); + } + + result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); + + state->in_preprocessor = false; + + return(result); +} + +function Code_Index_Nest* +generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state){ + Token *token = token_it_read(&state->it); + Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); + result->kind = CodeIndexNest_Scope; + result->open = Ii64(token); + result->close = Ii64(max_i64); + result->file = index; + + state->scope_counter += 1; + + generic_parse_inc(state); + for (;;){ + generic_parse_skip_soft_tokens(index, state); + token = token_it_read(&state->it); + if (token == 0 || state->finished){ + break; + } + + if (state->in_preprocessor){ + if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || + token->kind == TokenBaseKind_Preprocessor){ + break; + } + } + else{ + if (token->kind == TokenBaseKind_Preprocessor){ + Code_Index_Nest *nest = generic_parse_preprocessor(index, state); + code_index_push_nest(&index->nest_list, nest); + continue; + } + } + + if (token->kind == TokenBaseKind_ScopeClose){ + result->is_closed = true; + result->close = Ii64(token); + generic_parse_inc(state); + break; + } + + if (token->kind == TokenBaseKind_ScopeOpen){ + Code_Index_Nest *nest = generic_parse_scope(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + if (token->kind == TokenBaseKind_ParentheticalOpen){ + Code_Index_Nest *nest = generic_parse_paren(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + if (token->kind == TokenBaseKind_ParentheticalClose){ + generic_parse_inc(state); + continue; + } + + { + Code_Index_Nest *nest = generic_parse_statement(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + } + } + + result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); + + state->scope_counter -= 1; + + return(result); +} + +function Code_Index_Nest* +generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state){ + Token *token = token_it_read(&state->it); + Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); + result->kind = CodeIndexNest_Paren; + result->open = Ii64(token); + result->close = Ii64(max_i64); + result->file = index; + + i64 manifested_characters_on_line = 0; + { + u8 *ptr = state->prev_line_start; + u8 *end_ptr = state->contents.str + token->pos; + // NOTE(allen): Initial whitespace + for (;ptr < end_ptr; ptr += 1){ + if (!character_is_whitespace(*ptr)){ + break; + } + } + // NOTE(allen): Manifested characters + manifested_characters_on_line = (i64)(end_ptr - ptr) + token->size; + } + + state->paren_counter += 1; + + generic_parse_inc(state); + for (;;){ + generic_parse_skip_soft_tokens(index, state); + token = token_it_read(&state->it); + if (token == 0 || state->finished){ + break; + } + + if (state->in_preprocessor){ + if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || + token->kind == TokenBaseKind_Preprocessor){ + break; + } + } + else{ + if (token->kind == TokenBaseKind_Preprocessor){ + Code_Index_Nest *nest = generic_parse_preprocessor(index, state); + code_index_push_nest(&index->nest_list, nest); + continue; + } + } + + if (token->kind == TokenBaseKind_ParentheticalClose){ + result->is_closed = true; + result->close = Ii64(token); + generic_parse_inc(state); + break; + } + + if (token->kind == TokenBaseKind_ScopeClose){ + break; + } + + if (token->kind == TokenBaseKind_ScopeOpen){ + Code_Index_Nest *nest = generic_parse_scope(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + if (token->kind == TokenBaseKind_ParentheticalOpen){ + Code_Index_Nest *nest = generic_parse_paren(index, state); + nest->parent = result; + code_index_push_nest(&result->nest_list, nest); + continue; + } + + generic_parse_inc(state); + } + + result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); + + state->paren_counter -= 1; + + return(result); +} + +function b32 +generic_parse_full_input_breaks(Code_Index_File *index, Generic_Parse_State *state, i32 limit){ + b32 result = false; + + i64 first_index = token_it_index(&state->it); + i64 one_past_last_index = first_index + limit; + for (;;){ + generic_parse_skip_soft_tokens(index, state); + Token *token = token_it_read(&state->it); + + if (token == 0 || state->finished){ + result = true; + break; + } + + if (token->kind == TokenBaseKind_Preprocessor){ + Code_Index_Nest *nest = generic_parse_preprocessor(index, state); + code_index_push_nest(&index->nest_list, nest); + } + else if (token->kind == TokenBaseKind_ScopeOpen){ + Code_Index_Nest *nest = generic_parse_scope(index, state); + code_index_push_nest(&index->nest_list, nest); + } + else if (token->kind == TokenBaseKind_ParentheticalOpen){ + Code_Index_Nest *nest = generic_parse_paren(index, state); + code_index_push_nest(&index->nest_list, nest); + } + else if (state->do_cpp_parse){ + if (token->sub_kind == TokenCppKind_Struct || + token->sub_kind == TokenCppKind_Union || + token->sub_kind == TokenCppKind_Enum){ + cpp_parse_type_structure(index, state, 0); + } + else if (token->sub_kind == TokenCppKind_Typedef){ + cpp_parse_type_def(index, state, 0); + } + else if (token->sub_kind == TokenCppKind_Identifier){ + cpp_parse_function(index, state, 0); + } + else{ + generic_parse_inc(state); + } + } + else{ + generic_parse_inc(state); + } + + i64 index = token_it_index(&state->it); + if (index >= one_past_last_index){ + token = token_it_read(&state->it); + if (token == 0){ + result = true; + } + break; + } + } + + if (result){ + index->nest_array = code_index_nest_ptr_array_from_list(state->arena, &index->nest_list); + index->note_array = code_index_note_ptr_array_from_list(state->arena, &index->note_list); + } + + return(result); +} + +//////////////////////////////// + +function void +default_comment_index(Application_Links *app, Arena *arena, Code_Index_File *index, Token *token, String_Const_u8 contents){ + +} + +function void +generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_State *state){ + generic_parse_init(app, arena, contents, tokens, default_comment_index, state); +} + +//////////////////////////////// + +function Token_Pair +layout_token_pair(Token_Array *tokens, i64 pos){ + Token_Pair result = {}; + Token_Iterator_Array it = token_iterator_pos(0, tokens, pos); + Token *b = token_it_read(&it); + if (b != 0){ + if (b->kind == TokenBaseKind_Whitespace){ + token_it_inc_non_whitespace(&it); + b = token_it_read(&it); + } + } + token_it_dec_non_whitespace(&it); + Token *a = token_it_read(&it); + if (a != 0){ + result.a = *a; + } + if (b != 0){ + result.b = *b; + } + return(result); +} + +function f32 +layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 space_advance, b32 *unresolved_dependence){ + f32 result = 0.f; + if (nest != 0){ + switch (nest->kind){ + case CodeIndexNest_Scope: + case CodeIndexNest_Preprocessor: + { + result = layout_index_x_shift(app, reflex, nest->parent, pos, space_advance, unresolved_dependence); + if (nest->open.min < pos && nest->open.max <= pos && + (!nest->is_closed || pos < nest->close.min)){ + result += 4.f*space_advance; + } + }break; + + case CodeIndexNest_Statement: + { + result = layout_index_x_shift(app, reflex, nest->parent, pos, space_advance, unresolved_dependence); + if (nest->open.min < pos && nest->open.max <= pos && + (!nest->is_closed || pos < nest->close.min)){ + result += 4.f*space_advance; + } + }break; + + case CodeIndexNest_Paren: + { + Rect_f32 box = layout_reflex_get_rect(app, reflex, nest->open.max - 1, unresolved_dependence); + result = box.x1; + }break; + } + } + return(result); +} + +function f32 +layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 space_advance){ + b32 ignore; + return(layout_index_x_shift(app, reflex, nest, pos, space_advance, &ignore)); +} + +function f32 +layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 space_advance, b32 *unresolved_dependence){ + f32 indent = 0; + Code_Index_Nest *nest = code_index_get_nest(file, pos); + if (nest != 0){ + indent = layout_index_x_shift(app, reflex, nest, pos, space_advance, unresolved_dependence); + } + return(indent); +} + +function f32 +layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 space_advance){ + b32 ignore; + return(layout_index_x_shift(app, reflex, file, pos, space_advance, &ignore)); +} + +function void +layout_index__emit_chunk(LefRig_TopBot_Layout_Vars *pos_vars, Face_ID face, Arena *arena, u8 *text_str, i64 range_first, u8 *ptr, u8 *end, Layout_Item_List *list){ + for (;ptr < end;){ + Character_Consume_Result consume = utf8_consume(ptr, (u64)(end - ptr)); + if (consume.codepoint != '\r'){ + i64 index = layout_index_from_ptr(ptr, text_str, range_first); + if (consume.codepoint != max_u32){ + lr_tb_write(pos_vars, face, arena, list, index, consume.codepoint); + } + else{ + lr_tb_write_byte(pos_vars, face, arena, list, index, *ptr); + } + } + ptr += consume.inc; + } +} + +function i32 +layout_token_score_wrap_token(Token_Pair *pair, Token_Cpp_Kind kind){ + i32 result = 0; + if (pair->a.sub_kind != kind && pair->b.sub_kind == kind){ + result -= 1; + } + else if (pair->a.sub_kind == kind && pair->b.sub_kind != kind){ + result += 1; + } + return(result); +} + +function Layout_Item_List +layout_index__inner(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Code_Index_File *file, Layout_Wrap_Kind kind){ + Scratch_Block scratch(app); + + Managed_Scope scope = buffer_get_managed_scope(app, buffer); + Token_Array *tokens_ptr = scope_attachment(app, scope, attachment_tokens, Token_Array); + + Layout_Item_List list = get_empty_item_list(range); + String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); + + Face_Advance_Map advance_map = get_face_advance_map(app, face); + Face_Metrics metrics = get_face_metrics(app, face); + LefRig_TopBot_Layout_Vars pos_vars = get_lr_tb_layout_vars(&advance_map, &metrics, width); + + f32 wrap_align_x = width - metrics.normal_advance; + + Layout_Reflex reflex = get_layout_reflex(&list, buffer, width, face); + + if (text.size == 0){ + lr_tb_write_blank(&pos_vars, face, arena, &list, range.start); + } + else{ + b32 first_of_the_line = true; + Newline_Layout_Vars newline_vars = get_newline_layout_vars(); + + u8 *ptr = text.str; + u8 *end_ptr = ptr + text.size; + u8 *word_ptr = ptr; + + u8 *pending_wrap_ptr = ptr; + f32 pending_wrap_x = 0.f; + i32 pending_wrap_paren_nest_count = 0; + i32 pending_wrap_token_score = 0; + f32 pending_wrap_accumulated_w = 0.f; + + start: + if (ptr == end_ptr){ + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); + goto finish; + } + + if (!character_is_whitespace(*ptr)){ + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); + goto consuming_non_whitespace; + } + + { + for (;ptr < end_ptr; ptr += 1){ + if (!character_is_whitespace(*ptr)){ + pending_wrap_ptr = ptr; + word_ptr = ptr; + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); + goto consuming_non_whitespace; + } + if (*ptr == '\n'){ + pending_wrap_ptr = ptr; + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); + goto consuming_normal_whitespace; + } + } + + if (ptr == end_ptr){ + pending_wrap_ptr = ptr; + i64 index = layout_index_from_ptr(ptr - 1, text.str, range.first); + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); + goto finish; + } + } + + consuming_non_whitespace: + { + for (;ptr <= end_ptr; ptr += 1){ + if (ptr == end_ptr || character_is_whitespace(*ptr)){ + break; + } + } + + // NOTE(allen): measure this word + newline_layout_consume_default(&newline_vars); + String_Const_u8 word = SCu8(word_ptr, ptr); + u8 *word_end = ptr; + { + f32 word_advance = 0.f; + ptr = word.str; + for (;ptr < word_end;){ + Character_Consume_Result consume = utf8_consume(ptr, (u64)(word_end - ptr)); + if (consume.codepoint != max_u32){ + word_advance += lr_tb_advance(&pos_vars, face, consume.codepoint); + } + else{ + word_advance += lr_tb_advance_byte(&pos_vars); + } + ptr += consume.inc; + } + pending_wrap_accumulated_w += word_advance; + } + + if (!first_of_the_line && (kind == Layout_Wrapped) && lr_tb_crosses_width(&pos_vars, pending_wrap_accumulated_w)){ + i64 index = layout_index_from_ptr(pending_wrap_ptr, text.str, range.first); + lr_tb_align_rightward(&pos_vars, wrap_align_x); + lr_tb_write_ghost(&pos_vars, face, arena, &list, index, '\\'); + + lr_tb_next_line(&pos_vars); +#if 0 + f32 shift = layout_index_x_shift(app, &reflex, file, index, metrics.space_advance); + lr_tb_advance_x_without_item(&pos_vars, shift); +#endif + + ptr = pending_wrap_ptr; + pending_wrap_accumulated_w = 0.f; + first_of_the_line = true; + goto start; + } + } + + consuming_normal_whitespace: + for (; ptr < end_ptr; ptr += 1){ + if (!character_is_whitespace(*ptr)){ + u8 *new_wrap_ptr = ptr; + + i64 index = layout_index_from_ptr(new_wrap_ptr, text.str, range.first); + Code_Index_Nest *new_wrap_nest = code_index_get_nest(file, index); + b32 invalid_wrap_x = false; + f32 new_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, metrics.space_advance, &invalid_wrap_x); + if (invalid_wrap_x){ + new_wrap_x = max_f32; + } + + i32 new_wrap_paren_nest_count = 0; + for (Code_Index_Nest *nest = new_wrap_nest; + nest != 0; + nest = nest->parent){ + if (nest->kind == CodeIndexNest_Paren){ + new_wrap_paren_nest_count += 1; + } + } + + Token_Pair new_wrap_token_pair = layout_token_pair(tokens_ptr, index); + + // TODO(allen): pull out the token scoring part and make it replacable for other + // language's token based wrap scoring needs. + i32 token_score = 0; + if (new_wrap_token_pair.a.kind == TokenBaseKind_Keyword){ + if (new_wrap_token_pair.b.kind == TokenBaseKind_ParentheticalOpen || + new_wrap_token_pair.b.kind == TokenBaseKind_Keyword){ + token_score -= 2; + } + } + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Eq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_PlusEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_MinusEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_StarEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_DivEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_ModEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_LeftLeftEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_RightRightEq); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Comma); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_AndAnd); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_OrOr); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Ternary); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Colon); + token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Semicolon); + + i32 new_wrap_token_score = token_score; + + b32 new_wrap_ptr_is_better = false; + if (first_of_the_line){ + new_wrap_ptr_is_better = true; + } + else{ + if (new_wrap_token_score > pending_wrap_token_score){ + new_wrap_ptr_is_better = true; + } + else if (new_wrap_token_score == pending_wrap_token_score){ + f32 new_score = new_wrap_paren_nest_count*10.f + new_wrap_x; + f32 old_score = pending_wrap_paren_nest_count*10.f + pending_wrap_x + metrics.normal_advance*4.f + pending_wrap_accumulated_w*0.5f; + + if (new_score < old_score){ + new_wrap_ptr_is_better = true; + } + } + } + + if (new_wrap_ptr_is_better){ + layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, new_wrap_ptr, &list); + first_of_the_line = false; + + pending_wrap_ptr = new_wrap_ptr; + pending_wrap_paren_nest_count = new_wrap_paren_nest_count; + pending_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, metrics.space_advance); + pending_wrap_paren_nest_count = new_wrap_paren_nest_count; + pending_wrap_token_score = new_wrap_token_score; + pending_wrap_accumulated_w = 0.f; + } + + word_ptr = ptr; + goto consuming_non_whitespace; + } + + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + switch (*ptr){ + default: + { + newline_layout_consume_default(&newline_vars); + pending_wrap_accumulated_w += lr_tb_advance(&pos_vars, face, *ptr); + }break; + + case '\r': + { + newline_layout_consume_CR(&newline_vars, index); + }break; + + case '\n': + { + layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); + pending_wrap_ptr = ptr + 1; + pending_wrap_accumulated_w = 0.f; + + u64 newline_index = newline_layout_consume_LF(&newline_vars, index); + lr_tb_write_blank(&pos_vars, face, arena, &list, newline_index); + lr_tb_next_line(&pos_vars); + first_of_the_line = true; + ptr += 1; + goto start; + }break; + } + } + + finish: + if (newline_layout_consume_finish(&newline_vars)){ + layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); + i64 index = layout_index_from_ptr(ptr, text.str, range.first); + lr_tb_write_blank(&pos_vars, face, arena, &list, index); + } + } + + layout_item_list_finish(&list, -pos_vars.line_to_text_shift); + + return(list); +} + +function Layout_Item_List +layout_virt_indent_index(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Layout_Wrap_Kind kind){ + Layout_Item_List result = {}; + + if (global_config.enable_virtual_whitespace){ + code_index_lock(); + Code_Index_File *file = code_index_get_file(buffer); + if (file != 0){ + result = layout_index__inner(app, arena, buffer, range, face, width, file, kind); + } + code_index_unlock(); + if (file == 0){ + result = layout_virt_indent_literal(app, arena, buffer, range, face, width, kind); + } + } + else{ + result = layout_basic(app, arena, buffer, range, face, width, kind); + } + + return(result); +} + +function Layout_Item_List +layout_virt_indent_index_unwrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ + return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Unwrapped)); +} + +function Layout_Item_List +layout_virt_indent_index_wrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ + return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Wrapped)); +} + +function Layout_Item_List +layout_virt_indent_index_generic(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ + Managed_Scope scope = buffer_get_managed_scope(app, buffer); + b32 *wrap_lines_ptr = scope_attachment(app, scope, buffer_wrap_lines, b32); + b32 wrap_lines = (wrap_lines_ptr != 0 && *wrap_lines_ptr); + return(layout_virt_indent_index(app, arena, buffer, range, face, width, wrap_lines?Layout_Wrapped:Layout_Unwrapped)); +} + +CUSTOM_COMMAND_SIG(toggle_virtual_whitespace) +CUSTOM_DOC("Toggles the current buffer's virtual whitespace status.") +{ + global_config.enable_virtual_whitespace = !global_config.enable_virtual_whitespace; + + for (Buffer_ID buffer = get_buffer_next(app, 0, Access_Always); + buffer != 0; + buffer = get_buffer_next(app, buffer, Access_Always)){ + buffer_clear_layout_cache(app, buffer); + } +} + +// BOTTOM + diff --git a/custom/4coder_draw.cpp b/custom/4coder_draw.cpp index 7ee104c6..c08b4b73 100644 --- a/custom/4coder_draw.cpp +++ b/custom/4coder_draw.cpp @@ -1,843 +1,843 @@ -/* -4coder_draw.cpp - Layout and rendering implementation of standard UI pieces (including buffers) -*/ - -// TOP - -function void -draw_text_layout_default(Application_Links *app, Text_Layout_ID layout_id){ - ARGB_Color special_color = finalize_color(defcolor_special_character, 0); - ARGB_Color ghost_color = finalize_color(defcolor_ghost_character, 0); - draw_text_layout(app, layout_id, special_color, ghost_color); -} - -function FColor -get_margin_color(i32 level){ - FColor margin = fcolor_zero(); - switch (level){ - default: - case UIHighlight_None: - { - margin = fcolor_id(defcolor_list_item); - }break; - case UIHighlight_Hover: - { - margin = fcolor_id(defcolor_list_item_hover); - }break; - case UIHighlight_Active: - { - margin = fcolor_id(defcolor_list_item_active); - }break; - } - return(margin); -} - -function Vec2_f32 -draw_string(Application_Links *app, Face_ID font_id, String_Const_u8 string, Vec2_f32 p, ARGB_Color color){ - return(draw_string_oriented(app, font_id, color, string, p, 0, V2f32(1.f, 0.f))); -} - -function Vec2_f32 -draw_string(Application_Links *app, Face_ID font_id, String_Const_u8 string, Vec2_f32 p, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_string(app, font_id, string, p, argb); -} - -function void -draw_rectangle_fcolor(Application_Links *app, Rect_f32 rect, f32 roundness, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_rectangle(app, rect, roundness, argb); -} - -function void -draw_rectangle_outline_fcolor(Application_Links *app, Rect_f32 rect, f32 roundness, f32 thickness, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_rectangle_outline(app, rect, roundness, thickness, argb); -} - -function void -draw_margin(Application_Links *app, Rect_f32 outer, Rect_f32 inner, ARGB_Color color){ - draw_rectangle(app, Rf32(outer.x0, outer.y0, outer.x1, inner.y0), 0.f, color); - draw_rectangle(app, Rf32(outer.x0, inner.y1, outer.x1, outer.y1), 0.f, color); - draw_rectangle(app, Rf32(outer.x0, inner.y0, inner.x0, inner.y1), 0.f, color); - draw_rectangle(app, Rf32(inner.x1, inner.y0, outer.x1, inner.y1), 0.f, color); -} - -function void -draw_margin(Application_Links *app, Rect_f32 outer, Rect_f32 inner, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_margin(app, outer, inner, argb); -} - -function void -draw_character_block(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, ARGB_Color color){ - Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); - draw_rectangle(app, rect, roundness, color); -} - -function void -draw_character_block(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_character_block(app, layout, pos, roundness, argb); -} - -function void -draw_character_block(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, ARGB_Color color){ - if (range.first < range.one_past_last){ - i64 i = range.first; - Rect_f32 first_rect = text_layout_character_on_screen(app, layout, i); - i += 1; - Range_f32 y = rect_range_y(first_rect); - Range_f32 x = rect_range_x(first_rect); - for (;i < range.one_past_last; i += 1){ - Rect_f32 rect = text_layout_character_on_screen(app, layout, i); - if (rect.x0 < rect.x1 && rect.y0 < rect.y1){ - Range_f32 new_y = rect_range_y(rect); - Range_f32 new_x = rect_range_x(rect); - b32 joinable = false; - if (new_y == y && (range_overlap(x, new_x) || x.max == new_x.min || new_x.max == x.min)){ - joinable = true; - } - - if (!joinable){ - draw_rectangle(app, Rf32(x, y), roundness, color); - y = new_y; - x = new_x; - } - else{ - x = range_union(x, new_x); - } - } - } - draw_rectangle(app, Rf32(x, y), roundness, color); - } - for (i64 i = range.first; i < range.one_past_last; i += 1){ - draw_character_block(app, layout, i, roundness, color); - } -} - -function void -draw_character_block(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_character_block(app, layout, range, roundness, argb); - } - -function void -draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, f32 thickness, ARGB_Color color){ - Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); - draw_rectangle_outline(app, rect, roundness, thickness, color); -} - -function void -draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, f32 thickness, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_character_wire_frame(app, layout, pos, roundness, thickness, argb); -} - -function void -draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, f32 thickness, FColor color){ - for (i64 i = range.first; i < range.one_past_last; i += 1){ - draw_character_wire_frame(app, layout, i, roundness, thickness, color); - } -} - -function void -draw_character_i_bar(Application_Links *app, Text_Layout_ID layout, i64 pos, ARGB_Color color){ - Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); - rect.x1 = rect.x0 + 1.f; - draw_rectangle(app, rect, 0.f, color); -} - -function void -draw_character_i_bar(Application_Links *app, Text_Layout_ID layout, i64 pos, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_character_i_bar(app, layout, pos, argb); -} - -function void -draw_line_highlight(Application_Links *app, Text_Layout_ID layout, Range_i64 line_range, ARGB_Color color){ - Range_f32 y1 = text_layout_line_on_screen(app, layout, line_range.min); - Range_f32 y2 = text_layout_line_on_screen(app, layout, line_range.max); - Range_f32 y = range_union(y1, y2); - if (range_size(y) > 0.f){ - Rect_f32 region = text_layout_region(app, layout); - draw_rectangle(app, Rf32(rect_range_x(region), y), 0.f, color); - } -} - -function void -draw_line_highlight(Application_Links *app, Text_Layout_ID layout, Range_i64 line_range, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - draw_line_highlight(app, layout, line_range, argb); -} - -function void -draw_line_highlight(Application_Links *app, Text_Layout_ID layout, i64 line, ARGB_Color color){ - draw_line_highlight(app, layout, Ii64(line), color); -} - -function void -draw_line_highlight(Application_Links *app, Text_Layout_ID layout, i64 line, FColor color){ - draw_line_highlight(app, layout, Ii64(line), color); -} - -function void -paint_text_color_fcolor(Application_Links *app, Text_Layout_ID layout, Range_i64 pos, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - paint_text_color(app, layout, pos, argb); -} - -function void -paint_text_color_pos(Application_Links *app, Text_Layout_ID layout, i64 pos, ARGB_Color color){ - paint_text_color(app, layout, Ii64(pos, pos + 1), color); -} - -function void -paint_text_color_pos(Application_Links *app, Text_Layout_ID layout, i64 pos, FColor color){ - ARGB_Color argb = fcolor_resolve(color); - paint_text_color_pos(app, layout, pos, argb); -} - -//////////////////////////////// - -function Rect_f32_Pair -layout_file_bar_on_top(Rect_f32 rect, f32 line_height){ - return(rect_split_top_bottom(rect, line_height + 2.f)); -} - -function Rect_f32_Pair -layout_file_bar_on_bot(Rect_f32 rect, f32 line_height){ - return(rect_split_top_bottom_neg(rect, line_height + 2.f)); -} - -function Rect_f32_Pair -layout_query_bar_on_top(Rect_f32 rect, f32 line_height, i32 bar_count){ - return(rect_split_top_bottom(rect, (line_height + 2.f)*bar_count)); -} - -function Rect_f32_Pair -layout_query_bar_on_bot(Rect_f32 rect, f32 line_height, i32 bar_count){ - return(rect_split_top_bottom_neg(rect, (line_height + 2.f)*bar_count)); -} - -function Rect_f32_Pair -layout_line_number_margin(Rect_f32 rect, f32 digit_advance, i64 digit_count){ - f32 margin_width = (f32)digit_count*digit_advance + 2.f; - return(rect_split_left_right(rect, margin_width)); -} - -function Rect_f32_Pair -layout_line_number_margin(Application_Links *app, Buffer_ID buffer, Rect_f32 rect, f32 digit_advance){ - i64 line_count = buffer_get_line_count(app, buffer); - i64 line_count_digit_count = digit_count_from_integer(line_count, 10); - return(layout_line_number_margin(rect, digit_advance, line_count_digit_count)); -} - -global_const i32 fps_history_depth = 10; -function Rect_f32_Pair -layout_fps_hud_on_bottom(Rect_f32 rect, f32 line_height){ - return(rect_split_top_bottom_neg(rect, line_height*fps_history_depth)); -} - -function Rect_f32 -draw_background_and_margin(Application_Links *app, View_ID view, ARGB_Color margin, ARGB_Color back){ - Rect_f32 view_rect = view_get_screen_rect(app, view); - Rect_f32 inner = rect_inner(view_rect, 3.f); - draw_rectangle(app, inner, 0.f, back); - draw_margin(app, view_rect, inner, margin); - return(inner); -} - -function Rect_f32 -draw_background_and_margin(Application_Links *app, View_ID view, FColor margin, FColor back){ - ARGB_Color margin_argb = fcolor_resolve(margin); - ARGB_Color back_argb = fcolor_resolve(back); - return(draw_background_and_margin(app, view, margin_argb, back_argb)); -} - -function Rect_f32 -draw_background_and_margin(Application_Links *app, View_ID view, b32 is_active_view){ - FColor margin_color = get_margin_color(is_active_view?UIHighlight_Active:UIHighlight_None); - return(draw_background_and_margin(app, view, margin_color, fcolor_id(defcolor_back))); -} - -function Rect_f32 -draw_background_and_margin(Application_Links *app, View_ID view){ - View_ID active_view = get_active_view(app, Access_Always); - b32 is_active_view = (active_view == view); - return(draw_background_and_margin(app, view, is_active_view)); -} - -function void -draw_file_bar(Application_Links *app, View_ID view_id, Buffer_ID buffer, Face_ID face_id, Rect_f32 bar){ - Scratch_Block scratch(app); - - draw_rectangle_fcolor(app, bar, 0.f, fcolor_id(defcolor_bar)); - - FColor base_color = fcolor_id(defcolor_base); - FColor pop2_color = fcolor_id(defcolor_pop2); - - i64 cursor_position = view_get_cursor_pos(app, view_id); - Buffer_Cursor cursor = view_compute_cursor(app, view_id, seek_pos(cursor_position)); - - Fancy_Line list = {}; - String_Const_u8 unique_name = push_buffer_unique_name(app, scratch, buffer); - push_fancy_string(scratch, &list, base_color, unique_name); - push_fancy_stringf(scratch, &list, base_color, " - Row: %3.lld Col: %3.lld -", cursor.line, cursor.col); - - Managed_Scope scope = buffer_get_managed_scope(app, buffer); - Line_Ending_Kind *eol_setting = scope_attachment(app, scope, buffer_eol_setting, - Line_Ending_Kind); - switch (*eol_setting){ - case LineEndingKind_Binary: - { - push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" bin")); - }break; - - case LineEndingKind_LF: - { - push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" lf")); - }break; - - case LineEndingKind_CRLF: - { - push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" crlf")); - }break; - } - - { - Dirty_State dirty = buffer_get_dirty_state(app, buffer); - u8 space[3]; - String_u8 str = Su8(space, 0, 3); - if (dirty != 0){ - string_append(&str, string_u8_litexpr(" ")); - } - if (HasFlag(dirty, DirtyState_UnsavedChanges)){ - string_append(&str, string_u8_litexpr("*")); - } - if (HasFlag(dirty, DirtyState_UnloadedChanges)){ - string_append(&str, string_u8_litexpr("!")); - } - push_fancy_string(scratch, &list, pop2_color, str.string); - } - - Vec2_f32 p = bar.p0 + V2f32(2.f, 2.f); - draw_fancy_line(app, face_id, fcolor_zero(), &list, p); -} - -function void -draw_query_bar(Application_Links *app, Query_Bar *query_bar, Face_ID face_id, Rect_f32 bar){ - Scratch_Block scratch(app); - Fancy_Line list = {}; - push_fancy_string(scratch, &list, fcolor_id(defcolor_pop1) , query_bar->prompt); - push_fancy_string(scratch, &list, fcolor_id(defcolor_text_default), query_bar->string); - Vec2_f32 p = bar.p0 + V2f32(2.f, 2.f); - draw_fancy_line(app, face_id, fcolor_zero(), &list, p); -} - -function void -draw_line_number_margin(Application_Links *app, View_ID view_id, Buffer_ID buffer, Face_ID face_id, Text_Layout_ID text_layout_id, Rect_f32 margin){ - Rect_f32 prev_clip = draw_set_clip(app, margin); - draw_rectangle_fcolor(app, margin, 0.f, fcolor_id(defcolor_line_numbers_back)); - - Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); - - FColor line_color = fcolor_id(defcolor_line_numbers_text); - - i64 line_count = buffer_get_line_count(app, buffer); - i64 line_count_digit_count = digit_count_from_integer(line_count, 10); - - Scratch_Block scratch(app, Scratch_Share); - - Buffer_Cursor cursor = view_compute_cursor(app, view_id, seek_pos(visible_range.first)); - i64 line_number = cursor.line; - for (;cursor.pos <= visible_range.one_past_last;){ - if (line_number > line_count){ - break; - } - Range_f32 line_y = text_layout_line_on_screen(app, text_layout_id, line_number); - Vec2_f32 p = V2f32(margin.x0, line_y.min); - Temp_Memory_Block temp(scratch); - Fancy_String *string = push_fancy_stringf(scratch, 0, line_color, - "%*lld", - line_count_digit_count, - line_number); - draw_fancy_string(app, face_id, fcolor_zero(), string, p); - line_number += 1; - } - - draw_set_clip(app, prev_clip); -} - -function void -draw_fps_hud(Application_Links *app, Frame_Info frame_info, - Face_ID face_id, Rect_f32 rect){ - Face_Metrics face_metrics = get_face_metrics(app, face_id); - f32 line_height = face_metrics.line_height; - - local_persist f32 history_literal_dt[fps_history_depth] = {}; - local_persist f32 history_animation_dt[fps_history_depth] = {}; - local_persist i32 history_frame_index[fps_history_depth] = {}; - - i32 wrapped_index = frame_info.index%fps_history_depth; - history_literal_dt[wrapped_index] = frame_info.literal_dt; - history_animation_dt[wrapped_index] = frame_info.animation_dt; - history_frame_index[wrapped_index] = frame_info.index; - - draw_rectangle_fcolor(app, rect, 0.f, f_black); - draw_rectangle_outline_fcolor(app, rect, 0.f, 1.f, f_white); - - Vec2_f32 p = rect.p0; - - Scratch_Block scratch(app); - - Range_i32 ranges[2] = {}; - ranges[0].first = wrapped_index; - ranges[0].one_past_last = -1; - ranges[1].first = fps_history_depth - 1; - ranges[1].one_past_last = wrapped_index; - for (i32 i = 0; i < 2; i += 1){ - Range_i32 r = ranges[i]; - for (i32 j = r.first; j > r.one_past_last; j -= 1, p.y += line_height){ - f32 dts[2]; - dts[0] = history_literal_dt[j]; - dts[1] = history_animation_dt[j]; - i32 frame_index = history_frame_index[j]; - - Fancy_Line list = {}; - push_fancy_stringf(scratch, &list, f_pink , "FPS: "); - push_fancy_stringf(scratch, &list, f_green, "["); - push_fancy_stringf(scratch, &list, f_white, "%5d", frame_index); - push_fancy_stringf(scratch, &list, f_green, "]: "); - - for (i32 k = 0; k < 2; k += 1){ - f32 dt = dts[k]; - if (dt == 0.f){ - push_fancy_stringf(scratch, &list, f_white, "----------"); - } - else{ - push_fancy_stringf(scratch, &list, f_white, "%10.6f", dt); - } - push_fancy_stringf(scratch, &list, f_green, " | "); - } - - draw_fancy_line(app, face_id, fcolor_zero(), &list, p); - } - } -} - -function FColor -get_token_color_cpp(Token token){ - Managed_ID color = defcolor_text_default; - switch (token.kind){ - case TokenBaseKind_Preprocessor: - { - color = defcolor_preproc; - }break; - case TokenBaseKind_Keyword: - { - color = defcolor_keyword; - }break; - case TokenBaseKind_Comment: - { - color = defcolor_comment; - }break; - case TokenBaseKind_LiteralString: - { - color = defcolor_str_constant; - }break; - case TokenBaseKind_LiteralInteger: - { - color = defcolor_int_constant; - }break; - case TokenBaseKind_LiteralFloat: - { - color = defcolor_float_constant; - }break; - default: - { - switch (token.sub_kind){ - case TokenCppKind_LiteralTrue: - case TokenCppKind_LiteralFalse: - { - color = defcolor_bool_constant; - }break; - case TokenCppKind_LiteralCharacter: - case TokenCppKind_LiteralCharacterWide: - case TokenCppKind_LiteralCharacterUTF8: - case TokenCppKind_LiteralCharacterUTF16: - case TokenCppKind_LiteralCharacterUTF32: - { - color = defcolor_char_constant; - }break; - case TokenCppKind_PPIncludeFile: - { - color = defcolor_include; - }break; - } - }break; - } - return(fcolor_id(color)); -} - -function void -draw_cpp_token_colors(Application_Links *app, Text_Layout_ID text_layout_id, Token_Array *array){ - Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); - i64 first_index = token_index_from_pos(array, visible_range.first); - Token_Iterator_Array it = token_iterator_index(0, array, first_index); - for (;;){ - Token *token = token_it_read(&it); - if (token->pos >= visible_range.one_past_last){ - break; - } - FColor color = get_token_color_cpp(*token); - ARGB_Color argb = fcolor_resolve(color); - paint_text_color(app, text_layout_id, Ii64_size(token->pos, token->size), argb); - if (!token_it_inc_all(&it)){ - break; - } - } -} - -function void -draw_comment_highlights(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, - Token_Array *array, Comment_Highlight_Pair *pairs, i32 pair_count){ - Scratch_Block scratch(app); - Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); - i64 first_index = token_index_from_pos(array, visible_range.first); - Token_Iterator_Array it = token_iterator_index(buffer, array, first_index); - for (;;){ - Temp_Memory_Block temp(scratch); - Token *token = token_it_read(&it); - if (token->pos >= visible_range.one_past_last){ - break; - } - String_Const_u8 tail = {}; - if (token_it_check_and_get_lexeme(app, scratch, &it, TokenBaseKind_Comment, &tail)){ - for (i64 index = token->pos; - tail.size > 0; - tail = string_skip(tail, 1), index += 1){ - Comment_Highlight_Pair *pair = pairs; - for (i32 i = 0; i < pair_count; i += 1, pair += 1){ - u64 needle_size = pair->needle.size; - if (needle_size == 0){ - continue; - } - String_Const_u8 prefix = string_prefix(tail, needle_size); - if (string_match(prefix, pair->needle)){ - Range_i64 range = Ii64_size(index, needle_size); - paint_text_color(app, text_layout_id, range, pair->color); - tail = string_skip(tail, needle_size - 1); - index += needle_size - 1; - break; - } - } - } - } - if (!token_it_inc_non_whitespace(&it)){ - break; - } - } -} - -function Range_i64_Array -get_enclosure_ranges(Application_Links *app, Arena *arena, Buffer_ID buffer, i64 pos, u32 flags){ - Range_i64_Array array = {}; - i32 max = 100; - array.ranges = push_array(arena, Range_i64, max); - for (;;){ - Range_i64 range = {}; - if (find_surrounding_nest(app, buffer, pos, flags, &range)){ - array.ranges[array.count] = range; - array.count += 1; - pos = range.first; - if (array.count >= max){ - break; - } - } - else{ - break; - } - } - return(array); -} - -function void -draw_enclosures(Application_Links *app, Text_Layout_ID text_layout_id, Buffer_ID buffer, - i64 pos, u32 flags, Range_Highlight_Kind kind, - ARGB_Color *back_colors, i32 back_count, - ARGB_Color *fore_colors, i32 fore_count){ - Scratch_Block scratch(app); - Range_i64_Array ranges = get_enclosure_ranges(app, scratch, buffer, pos, flags); - - i32 color_index = 0; - for (i32 i = ranges.count - 1; i >= 0; i -= 1){ - Range_i64 range = ranges.ranges[i]; - if (kind == RangeHighlightKind_LineHighlight){ - Range_i64 r[2] = {}; - if (i > 0){ - Range_i64 inner_range = ranges.ranges[i - 1]; - Range_i64 lines = get_line_range_from_pos_range(app, buffer, range); - Range_i64 inner_lines = get_line_range_from_pos_range(app, buffer, inner_range); - inner_lines.min = clamp_bot(lines.min, inner_lines.min); - inner_lines.max = clamp_top(inner_lines.max, lines.max); - inner_lines.min -= 1; - inner_lines.max += 1; - if (lines.min <= inner_lines.min){ - r[0] = Ii64(lines.min, inner_lines.min); - } - if (inner_lines.max <= lines.max){ - r[1] = Ii64(inner_lines.max, lines.max); - } - } - else{ - r[0] = get_line_range_from_pos_range(app, buffer, range); - } - for (i32 j = 0; j < 2; j += 1){ - if (r[j].min == 0){ - continue; - } - Range_i64 line_range = r[j]; - if (back_colors != 0){ - i32 back_index = color_index%back_count; - draw_line_highlight(app, text_layout_id, line_range, back_colors[back_index]); - } - if (fore_colors != 0){ - i32 fore_index = color_index%fore_count; - Range_i64 pos_range = get_pos_range_from_line_range(app, buffer, line_range); - paint_text_color(app, text_layout_id, pos_range, fore_colors[fore_index]); - } - } - } - else{ - if (back_colors != 0){ - i32 back_index = color_index%back_count; - draw_character_block(app, text_layout_id, range.min, 0.f, back_colors[back_index]); - draw_character_block(app, text_layout_id, range.max - 1, 0.f, back_colors[back_index]); - } - if (fore_colors != 0){ - i32 fore_index = color_index%fore_count; - paint_text_color_pos(app, text_layout_id, range.min, fore_colors[fore_index]); - paint_text_color_pos(app, text_layout_id, range.max - 1, fore_colors[fore_index]); - } - } - color_index += 1; - } -} - -function void -draw_scope_highlight(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, - i64 pos, ARGB_Color *colors, i32 color_count){ - draw_enclosures(app, text_layout_id, buffer, - pos, FindNest_Scope, RangeHighlightKind_LineHighlight, - colors, color_count, 0, 0); -} - -function void -draw_paren_highlight(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, - i64 pos, ARGB_Color *colors, i32 color_count){ - Token_Array token_array = get_token_array_from_buffer(app, buffer); - if (token_array.tokens != 0){ - Token_Iterator_Array it = token_iterator_pos(0, &token_array, pos); - Token *token = token_it_read(&it); - if (token != 0 && token->kind == TokenBaseKind_ParentheticalOpen){ - pos = token->pos + token->size; - } - else{ - if (token_it_dec_all(&it)){ - token = token_it_read(&it); - if (token->kind == TokenBaseKind_ParentheticalClose && - pos == token->pos + token->size){ - pos = token->pos; - } - } - } - } - draw_enclosures(app, text_layout_id, buffer, - pos, FindNest_Paren, RangeHighlightKind_CharacterHighlight, - 0, 0, colors, color_count); -} - -function void -draw_jump_highlights(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, - Buffer_ID jump_buffer, FColor line_color){ - Scratch_Block scratch(app); - if (jump_buffer != 0){ - Managed_Scope scopes[2]; - scopes[0] = buffer_get_managed_scope(app, jump_buffer); - scopes[1] = buffer_get_managed_scope(app, buffer); - Managed_Scope comp_scope = get_managed_scope_with_multiple_dependencies(app, scopes, ArrayCount(scopes)); - Managed_Object *markers_object = scope_attachment(app, comp_scope, sticky_jump_marker_handle, Managed_Object); - - i32 count = managed_object_get_item_count(app, *markers_object); - Marker *markers = push_array(scratch, Marker, count); - managed_object_load_data(app, *markers_object, 0, count, markers); - for (i32 i = 0; i < count; i += 1){ - i64 line_number = get_line_number_from_pos(app, buffer, markers[i].pos); - draw_line_highlight(app, text_layout_id, line_number, line_color); - } - } -} - -function b32 -draw_highlight_range(Application_Links *app, View_ID view_id, - Buffer_ID buffer, Text_Layout_ID text_layout_id, - f32 roundness){ - b32 has_highlight_range = false; - Managed_Scope scope = view_get_managed_scope(app, view_id); - Buffer_ID *highlight_buffer = scope_attachment(app, scope, view_highlight_buffer, Buffer_ID); - if (*highlight_buffer != 0){ - if (*highlight_buffer != buffer){ - view_disable_highlight_range(app, view_id); - } - else{ - has_highlight_range = true; - Managed_Object *highlight = scope_attachment(app, scope, view_highlight_range, Managed_Object); - Marker marker_range[2]; - if (managed_object_load_data(app, *highlight, 0, 2, marker_range)){ - Range_i64 range = Ii64(marker_range[0].pos, marker_range[1].pos); - draw_character_block(app, text_layout_id, range, roundness, - fcolor_id(defcolor_highlight)); - paint_text_color_fcolor(app, text_layout_id, range, - fcolor_id(defcolor_at_highlight)); - } - } - } - return(has_highlight_range); -} - -function void -draw_original_4coder_style_cursor_mark_highlight(Application_Links *app, View_ID view_id, b32 is_active_view, - Buffer_ID buffer, Text_Layout_ID text_layout_id, - f32 roundness, f32 outline_thickness){ - b32 has_highlight_range = draw_highlight_range(app, view_id, buffer, text_layout_id, roundness); - if (!has_highlight_range){ - i64 cursor_pos = view_get_cursor_pos(app, view_id); - i64 mark_pos = view_get_mark_pos(app, view_id); - if (is_active_view){ - draw_character_block(app, text_layout_id, cursor_pos, roundness, - fcolor_id(defcolor_cursor)); - paint_text_color_pos(app, text_layout_id, cursor_pos, - fcolor_id(defcolor_at_cursor)); - draw_character_wire_frame(app, text_layout_id, mark_pos, - roundness, outline_thickness, - fcolor_id(defcolor_mark)); - } - else{ - draw_character_wire_frame(app, text_layout_id, mark_pos, - roundness, outline_thickness, - fcolor_id(defcolor_mark)); - draw_character_wire_frame(app, text_layout_id, cursor_pos, - roundness, outline_thickness, - fcolor_id(defcolor_cursor)); - } - } -} - -function void -draw_notepad_style_cursor_highlight(Application_Links *app, View_ID view_id, - Buffer_ID buffer, Text_Layout_ID text_layout_id, - f32 roundness){ - b32 has_highlight_range = draw_highlight_range(app, view_id, buffer, text_layout_id, roundness); - if (!has_highlight_range){ - i64 cursor_pos = view_get_cursor_pos(app, view_id); - i64 mark_pos = view_get_mark_pos(app, view_id); - if (cursor_pos != mark_pos){ - Range_i64 range = Ii64(cursor_pos, mark_pos); - draw_character_block(app, text_layout_id, range, roundness, - fcolor_id(defcolor_highlight)); - paint_text_color_fcolor(app, text_layout_id, range, - fcolor_id(defcolor_at_highlight)); - } - draw_character_i_bar(app, text_layout_id, cursor_pos, fcolor_id(defcolor_cursor)); - } -} - -//////////////////////////////// - -function Rect_f32 -get_contained_box_near_point(Rect_f32 container, Vec2_f32 p, Vec2_f32 box_dims){ - Vec2_f32 container_dims = rect_dim(container); - box_dims.x = clamp_top(box_dims.x, container_dims.x); - box_dims.y = clamp_top(box_dims.y, container_dims.y); - Vec2_f32 q = p + V2f32(-20.f, 22.f); - if (q.x + box_dims.x > container.x1){ - q.x = container.x1 - box_dims.x; - } - if (q.y + box_dims.y > container.y1){ - q.y = p.y - box_dims.y - 2.f; - if (q.y < container.y0){ - q.y = (container.y0 + container.y1 - box_dims.y)*0.5f; - } - } - return(Rf32_xy_wh(q, box_dims)); -} - -function Rect_f32 -draw_tool_tip(Application_Links *app, Face_ID face, Fancy_Block *block, - Vec2_f32 p, Rect_f32 region, f32 x_padding, f32 x_half_padding, - FColor back_color){ - Rect_f32 box = Rf32(p, p); - if (block->line_count > 0){ - Vec2_f32 dims = get_fancy_block_dim(app, face, block); - dims += V2f32(x_padding, 2.f); - box = get_contained_box_near_point(region, p, dims); - box.x0 = f32_round32(box.x0); - box.y0 = f32_round32(box.y0); - box.x1 = f32_round32(box.x1); - box.y1 = f32_round32(box.y1); - Rect_f32 prev_clip = draw_set_clip(app, box); - draw_rectangle_fcolor(app, box, 6.f, back_color); - draw_fancy_block(app, face, fcolor_zero(), block, - box.p0 + V2f32(x_half_padding, 1.f)); - draw_set_clip(app, prev_clip); - } - return(box); -} - -function Rect_f32 -draw_drop_down(Application_Links *app, Face_ID face, Fancy_Block *block, - Vec2_f32 p, Rect_f32 region, f32 x_padding, f32 x_half_padding, - FColor outline_color, FColor back_color){ - Rect_f32 box = Rf32(p, p); - if (block->line_count > 0){ - Vec2_f32 dims = get_fancy_block_dim(app, face, block); - dims += V2f32(x_padding, 4.f); - box = get_contained_box_near_point(region, p, dims); - box.x0 = f32_round32(box.x0); - box.y0 = f32_round32(box.y0); - box.x1 = f32_round32(box.x1); - box.y1 = f32_round32(box.y1); - Rect_f32 prev_clip = draw_set_clip(app, box); - draw_rectangle_fcolor(app, box, 0.f, back_color); - draw_margin(app, box, rect_inner(box, 1.f), outline_color); - draw_fancy_block(app, face, fcolor_zero(), block, - box.p0 + V2f32(x_half_padding, 2.f)); - draw_set_clip(app, prev_clip); - } - return(box); -} - -function b32 -draw_button(Application_Links *app, Rect_f32 rect, Vec2_f32 mouse_p, Face_ID face, String_Const_u8 text){ - b32 hovered = false; - if (rect_contains_point(rect, mouse_p)){ - hovered = true; - } - - FColor margin_color = get_margin_color(hovered?UIHighlight_Active:UIHighlight_None); - draw_rectangle_fcolor(app, rect, 3.f, margin_color); - rect = rect_inner(rect, 3.f); - draw_rectangle_fcolor(app, rect, 3.f, fcolor_id(defcolor_back)); - - Scratch_Block scratch(app); - Fancy_String *fancy = push_fancy_string(scratch, 0, face, fcolor_id(defcolor_text_default), text); - Vec2_f32 dim = get_fancy_string_dim(app, 0, fancy); - Vec2_f32 p = (rect.p0 + rect.p1 - dim)*0.5f; - draw_fancy_string(app, fancy, p); - - return(hovered); -} - -// BOTTOM - +/* +4coder_draw.cpp - Layout and rendering implementation of standard UI pieces (including buffers) +*/ + +// TOP + +function void +draw_text_layout_default(Application_Links *app, Text_Layout_ID layout_id){ + ARGB_Color special_color = finalize_color(defcolor_special_character, 0); + ARGB_Color ghost_color = finalize_color(defcolor_ghost_character, 0); + draw_text_layout(app, layout_id, special_color, ghost_color); +} + +function FColor +get_margin_color(i32 level){ + FColor margin = fcolor_zero(); + switch (level){ + default: + case UIHighlight_None: + { + margin = fcolor_id(defcolor_list_item); + }break; + case UIHighlight_Hover: + { + margin = fcolor_id(defcolor_list_item_hover); + }break; + case UIHighlight_Active: + { + margin = fcolor_id(defcolor_list_item_active); + }break; + } + return(margin); +} + +function Vec2_f32 +draw_string(Application_Links *app, Face_ID font_id, String_Const_u8 string, Vec2_f32 p, ARGB_Color color){ + return(draw_string_oriented(app, font_id, color, string, p, 0, V2f32(1.f, 0.f))); +} + +function Vec2_f32 +draw_string(Application_Links *app, Face_ID font_id, String_Const_u8 string, Vec2_f32 p, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + return(draw_string(app, font_id, string, p, argb)); +} + +function void +draw_rectangle_fcolor(Application_Links *app, Rect_f32 rect, f32 roundness, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_rectangle(app, rect, roundness, argb); +} + +function void +draw_rectangle_outline_fcolor(Application_Links *app, Rect_f32 rect, f32 roundness, f32 thickness, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_rectangle_outline(app, rect, roundness, thickness, argb); +} + +function void +draw_margin(Application_Links *app, Rect_f32 outer, Rect_f32 inner, ARGB_Color color){ + draw_rectangle(app, Rf32(outer.x0, outer.y0, outer.x1, inner.y0), 0.f, color); + draw_rectangle(app, Rf32(outer.x0, inner.y1, outer.x1, outer.y1), 0.f, color); + draw_rectangle(app, Rf32(outer.x0, inner.y0, inner.x0, inner.y1), 0.f, color); + draw_rectangle(app, Rf32(inner.x1, inner.y0, outer.x1, inner.y1), 0.f, color); +} + +function void +draw_margin(Application_Links *app, Rect_f32 outer, Rect_f32 inner, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_margin(app, outer, inner, argb); +} + +function void +draw_character_block(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, ARGB_Color color){ + Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); + draw_rectangle(app, rect, roundness, color); +} + +function void +draw_character_block(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_character_block(app, layout, pos, roundness, argb); +} + +function void +draw_character_block(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, ARGB_Color color){ + if (range.first < range.one_past_last){ + i64 i = range.first; + Rect_f32 first_rect = text_layout_character_on_screen(app, layout, i); + i += 1; + Range_f32 y = rect_range_y(first_rect); + Range_f32 x = rect_range_x(first_rect); + for (;i < range.one_past_last; i += 1){ + Rect_f32 rect = text_layout_character_on_screen(app, layout, i); + if (rect.x0 < rect.x1 && rect.y0 < rect.y1){ + Range_f32 new_y = rect_range_y(rect); + Range_f32 new_x = rect_range_x(rect); + b32 joinable = false; + if (new_y == y && (range_overlap(x, new_x) || x.max == new_x.min || new_x.max == x.min)){ + joinable = true; + } + + if (!joinable){ + draw_rectangle(app, Rf32(x, y), roundness, color); + y = new_y; + x = new_x; + } + else{ + x = range_union(x, new_x); + } + } + } + draw_rectangle(app, Rf32(x, y), roundness, color); + } + for (i64 i = range.first; i < range.one_past_last; i += 1){ + draw_character_block(app, layout, i, roundness, color); + } +} + +function void +draw_character_block(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_character_block(app, layout, range, roundness, argb); +} + +function void +draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, f32 thickness, ARGB_Color color){ + Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); + draw_rectangle_outline(app, rect, roundness, thickness, color); +} + +function void +draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, i64 pos, f32 roundness, f32 thickness, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_character_wire_frame(app, layout, pos, roundness, thickness, argb); +} + +function void +draw_character_wire_frame(Application_Links *app, Text_Layout_ID layout, Range_i64 range, f32 roundness, f32 thickness, FColor color){ + for (i64 i = range.first; i < range.one_past_last; i += 1){ + draw_character_wire_frame(app, layout, i, roundness, thickness, color); + } +} + +function void +draw_character_i_bar(Application_Links *app, Text_Layout_ID layout, i64 pos, ARGB_Color color){ + Rect_f32 rect = text_layout_character_on_screen(app, layout, pos); + rect.x1 = rect.x0 + 1.f; + draw_rectangle(app, rect, 0.f, color); +} + +function void +draw_character_i_bar(Application_Links *app, Text_Layout_ID layout, i64 pos, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_character_i_bar(app, layout, pos, argb); +} + +function void +draw_line_highlight(Application_Links *app, Text_Layout_ID layout, Range_i64 line_range, ARGB_Color color){ + Range_f32 y1 = text_layout_line_on_screen(app, layout, line_range.min); + Range_f32 y2 = text_layout_line_on_screen(app, layout, line_range.max); + Range_f32 y = range_union(y1, y2); + if (range_size(y) > 0.f){ + Rect_f32 region = text_layout_region(app, layout); + draw_rectangle(app, Rf32(rect_range_x(region), y), 0.f, color); + } +} + +function void +draw_line_highlight(Application_Links *app, Text_Layout_ID layout, Range_i64 line_range, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + draw_line_highlight(app, layout, line_range, argb); +} + +function void +draw_line_highlight(Application_Links *app, Text_Layout_ID layout, i64 line, ARGB_Color color){ + draw_line_highlight(app, layout, Ii64(line), color); +} + +function void +draw_line_highlight(Application_Links *app, Text_Layout_ID layout, i64 line, FColor color){ + draw_line_highlight(app, layout, Ii64(line), color); +} + +function void +paint_text_color_fcolor(Application_Links *app, Text_Layout_ID layout, Range_i64 pos, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + paint_text_color(app, layout, pos, argb); +} + +function void +paint_text_color_pos(Application_Links *app, Text_Layout_ID layout, i64 pos, ARGB_Color color){ + paint_text_color(app, layout, Ii64(pos, pos + 1), color); +} + +function void +paint_text_color_pos(Application_Links *app, Text_Layout_ID layout, i64 pos, FColor color){ + ARGB_Color argb = fcolor_resolve(color); + paint_text_color_pos(app, layout, pos, argb); +} + +//////////////////////////////// + +function Rect_f32_Pair +layout_file_bar_on_top(Rect_f32 rect, f32 line_height){ + return(rect_split_top_bottom(rect, line_height + 2.f)); +} + +function Rect_f32_Pair +layout_file_bar_on_bot(Rect_f32 rect, f32 line_height){ + return(rect_split_top_bottom_neg(rect, line_height + 2.f)); +} + +function Rect_f32_Pair +layout_query_bar_on_top(Rect_f32 rect, f32 line_height, i32 bar_count){ + return(rect_split_top_bottom(rect, (line_height + 2.f)*bar_count)); +} + +function Rect_f32_Pair +layout_query_bar_on_bot(Rect_f32 rect, f32 line_height, i32 bar_count){ + return(rect_split_top_bottom_neg(rect, (line_height + 2.f)*bar_count)); +} + +function Rect_f32_Pair +layout_line_number_margin(Rect_f32 rect, f32 digit_advance, i64 digit_count){ + f32 margin_width = (f32)digit_count*digit_advance + 2.f; + return(rect_split_left_right(rect, margin_width)); +} + +function Rect_f32_Pair +layout_line_number_margin(Application_Links *app, Buffer_ID buffer, Rect_f32 rect, f32 digit_advance){ + i64 line_count = buffer_get_line_count(app, buffer); + i64 line_count_digit_count = digit_count_from_integer(line_count, 10); + return(layout_line_number_margin(rect, digit_advance, line_count_digit_count)); +} + +global_const i32 fps_history_depth = 10; +function Rect_f32_Pair +layout_fps_hud_on_bottom(Rect_f32 rect, f32 line_height){ + return(rect_split_top_bottom_neg(rect, line_height*fps_history_depth)); +} + +function Rect_f32 +draw_background_and_margin(Application_Links *app, View_ID view, ARGB_Color margin, ARGB_Color back){ + Rect_f32 view_rect = view_get_screen_rect(app, view); + Rect_f32 inner = rect_inner(view_rect, 3.f); + draw_rectangle(app, inner, 0.f, back); + draw_margin(app, view_rect, inner, margin); + return(inner); +} + +function Rect_f32 +draw_background_and_margin(Application_Links *app, View_ID view, FColor margin, FColor back){ + ARGB_Color margin_argb = fcolor_resolve(margin); + ARGB_Color back_argb = fcolor_resolve(back); + return(draw_background_and_margin(app, view, margin_argb, back_argb)); +} + +function Rect_f32 +draw_background_and_margin(Application_Links *app, View_ID view, b32 is_active_view){ + FColor margin_color = get_margin_color(is_active_view?UIHighlight_Active:UIHighlight_None); + return(draw_background_and_margin(app, view, margin_color, fcolor_id(defcolor_back))); +} + +function Rect_f32 +draw_background_and_margin(Application_Links *app, View_ID view){ + View_ID active_view = get_active_view(app, Access_Always); + b32 is_active_view = (active_view == view); + return(draw_background_and_margin(app, view, is_active_view)); +} + +function void +draw_file_bar(Application_Links *app, View_ID view_id, Buffer_ID buffer, Face_ID face_id, Rect_f32 bar){ + Scratch_Block scratch(app); + + draw_rectangle_fcolor(app, bar, 0.f, fcolor_id(defcolor_bar)); + + FColor base_color = fcolor_id(defcolor_base); + FColor pop2_color = fcolor_id(defcolor_pop2); + + i64 cursor_position = view_get_cursor_pos(app, view_id); + Buffer_Cursor cursor = view_compute_cursor(app, view_id, seek_pos(cursor_position)); + + Fancy_Line list = {}; + String_Const_u8 unique_name = push_buffer_unique_name(app, scratch, buffer); + push_fancy_string(scratch, &list, base_color, unique_name); + push_fancy_stringf(scratch, &list, base_color, " - Row: %3.lld Col: %3.lld -", cursor.line, cursor.col); + + Managed_Scope scope = buffer_get_managed_scope(app, buffer); + Line_Ending_Kind *eol_setting = scope_attachment(app, scope, buffer_eol_setting, + Line_Ending_Kind); + switch (*eol_setting){ + case LineEndingKind_Binary: + { + push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" bin")); + }break; + + case LineEndingKind_LF: + { + push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" lf")); + }break; + + case LineEndingKind_CRLF: + { + push_fancy_string(scratch, &list, base_color, string_u8_litexpr(" crlf")); + }break; + } + + { + Dirty_State dirty = buffer_get_dirty_state(app, buffer); + u8 space[3]; + String_u8 str = Su8(space, 0, 3); + if (dirty != 0){ + string_append(&str, string_u8_litexpr(" ")); + } + if (HasFlag(dirty, DirtyState_UnsavedChanges)){ + string_append(&str, string_u8_litexpr("*")); + } + if (HasFlag(dirty, DirtyState_UnloadedChanges)){ + string_append(&str, string_u8_litexpr("!")); + } + push_fancy_string(scratch, &list, pop2_color, str.string); + } + + Vec2_f32 p = bar.p0 + V2f32(2.f, 2.f); + draw_fancy_line(app, face_id, fcolor_zero(), &list, p); +} + +function void +draw_query_bar(Application_Links *app, Query_Bar *query_bar, Face_ID face_id, Rect_f32 bar){ + Scratch_Block scratch(app); + Fancy_Line list = {}; + push_fancy_string(scratch, &list, fcolor_id(defcolor_pop1) , query_bar->prompt); + push_fancy_string(scratch, &list, fcolor_id(defcolor_text_default), query_bar->string); + Vec2_f32 p = bar.p0 + V2f32(2.f, 2.f); + draw_fancy_line(app, face_id, fcolor_zero(), &list, p); +} + +function void +draw_line_number_margin(Application_Links *app, View_ID view_id, Buffer_ID buffer, Face_ID face_id, Text_Layout_ID text_layout_id, Rect_f32 margin){ + Rect_f32 prev_clip = draw_set_clip(app, margin); + draw_rectangle_fcolor(app, margin, 0.f, fcolor_id(defcolor_line_numbers_back)); + + Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); + + FColor line_color = fcolor_id(defcolor_line_numbers_text); + + i64 line_count = buffer_get_line_count(app, buffer); + i64 line_count_digit_count = digit_count_from_integer(line_count, 10); + + Scratch_Block scratch(app, Scratch_Share); + + Buffer_Cursor cursor = view_compute_cursor(app, view_id, seek_pos(visible_range.first)); + i64 line_number = cursor.line; + for (;cursor.pos <= visible_range.one_past_last;){ + if (line_number > line_count){ + break; + } + Range_f32 line_y = text_layout_line_on_screen(app, text_layout_id, line_number); + Vec2_f32 p = V2f32(margin.x0, line_y.min); + Temp_Memory_Block temp(scratch); + Fancy_String *string = push_fancy_stringf(scratch, 0, line_color, + "%*lld", + line_count_digit_count, + line_number); + draw_fancy_string(app, face_id, fcolor_zero(), string, p); + line_number += 1; + } + + draw_set_clip(app, prev_clip); +} + +function void +draw_fps_hud(Application_Links *app, Frame_Info frame_info, + Face_ID face_id, Rect_f32 rect){ + Face_Metrics face_metrics = get_face_metrics(app, face_id); + f32 line_height = face_metrics.line_height; + + local_persist f32 history_literal_dt[fps_history_depth] = {}; + local_persist f32 history_animation_dt[fps_history_depth] = {}; + local_persist i32 history_frame_index[fps_history_depth] = {}; + + i32 wrapped_index = frame_info.index%fps_history_depth; + history_literal_dt[wrapped_index] = frame_info.literal_dt; + history_animation_dt[wrapped_index] = frame_info.animation_dt; + history_frame_index[wrapped_index] = frame_info.index; + + draw_rectangle_fcolor(app, rect, 0.f, f_black); + draw_rectangle_outline_fcolor(app, rect, 0.f, 1.f, f_white); + + Vec2_f32 p = rect.p0; + + Scratch_Block scratch(app); + + Range_i32 ranges[2] = {}; + ranges[0].first = wrapped_index; + ranges[0].one_past_last = -1; + ranges[1].first = fps_history_depth - 1; + ranges[1].one_past_last = wrapped_index; + for (i32 i = 0; i < 2; i += 1){ + Range_i32 r = ranges[i]; + for (i32 j = r.first; j > r.one_past_last; j -= 1, p.y += line_height){ + f32 dts[2]; + dts[0] = history_literal_dt[j]; + dts[1] = history_animation_dt[j]; + i32 frame_index = history_frame_index[j]; + + Fancy_Line list = {}; + push_fancy_stringf(scratch, &list, f_pink , "FPS: "); + push_fancy_stringf(scratch, &list, f_green, "["); + push_fancy_stringf(scratch, &list, f_white, "%5d", frame_index); + push_fancy_stringf(scratch, &list, f_green, "]: "); + + for (i32 k = 0; k < 2; k += 1){ + f32 dt = dts[k]; + if (dt == 0.f){ + push_fancy_stringf(scratch, &list, f_white, "----------"); + } + else{ + push_fancy_stringf(scratch, &list, f_white, "%10.6f", dt); + } + push_fancy_stringf(scratch, &list, f_green, " | "); + } + + draw_fancy_line(app, face_id, fcolor_zero(), &list, p); + } + } +} + +function FColor +get_token_color_cpp(Token token){ + Managed_ID color = defcolor_text_default; + switch (token.kind){ + case TokenBaseKind_Preprocessor: + { + color = defcolor_preproc; + }break; + case TokenBaseKind_Keyword: + { + color = defcolor_keyword; + }break; + case TokenBaseKind_Comment: + { + color = defcolor_comment; + }break; + case TokenBaseKind_LiteralString: + { + color = defcolor_str_constant; + }break; + case TokenBaseKind_LiteralInteger: + { + color = defcolor_int_constant; + }break; + case TokenBaseKind_LiteralFloat: + { + color = defcolor_float_constant; + }break; + default: + { + switch (token.sub_kind){ + case TokenCppKind_LiteralTrue: + case TokenCppKind_LiteralFalse: + { + color = defcolor_bool_constant; + }break; + case TokenCppKind_LiteralCharacter: + case TokenCppKind_LiteralCharacterWide: + case TokenCppKind_LiteralCharacterUTF8: + case TokenCppKind_LiteralCharacterUTF16: + case TokenCppKind_LiteralCharacterUTF32: + { + color = defcolor_char_constant; + }break; + case TokenCppKind_PPIncludeFile: + { + color = defcolor_include; + }break; + } + }break; + } + return(fcolor_id(color)); +} + +function void +draw_cpp_token_colors(Application_Links *app, Text_Layout_ID text_layout_id, Token_Array *array){ + Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); + i64 first_index = token_index_from_pos(array, visible_range.first); + Token_Iterator_Array it = token_iterator_index(0, array, first_index); + for (;;){ + Token *token = token_it_read(&it); + if (token->pos >= visible_range.one_past_last){ + break; + } + FColor color = get_token_color_cpp(*token); + ARGB_Color argb = fcolor_resolve(color); + paint_text_color(app, text_layout_id, Ii64_size(token->pos, token->size), argb); + if (!token_it_inc_all(&it)){ + break; + } + } +} + +function void +draw_comment_highlights(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, + Token_Array *array, Comment_Highlight_Pair *pairs, i32 pair_count){ + Scratch_Block scratch(app); + Range_i64 visible_range = text_layout_get_visible_range(app, text_layout_id); + i64 first_index = token_index_from_pos(array, visible_range.first); + Token_Iterator_Array it = token_iterator_index(buffer, array, first_index); + for (;;){ + Temp_Memory_Block temp(scratch); + Token *token = token_it_read(&it); + if (token->pos >= visible_range.one_past_last){ + break; + } + String_Const_u8 tail = {}; + if (token_it_check_and_get_lexeme(app, scratch, &it, TokenBaseKind_Comment, &tail)){ + for (i64 index = token->pos; + tail.size > 0; + tail = string_skip(tail, 1), index += 1){ + Comment_Highlight_Pair *pair = pairs; + for (i32 i = 0; i < pair_count; i += 1, pair += 1){ + u64 needle_size = pair->needle.size; + if (needle_size == 0){ + continue; + } + String_Const_u8 prefix = string_prefix(tail, needle_size); + if (string_match(prefix, pair->needle)){ + Range_i64 range = Ii64_size(index, needle_size); + paint_text_color(app, text_layout_id, range, pair->color); + tail = string_skip(tail, needle_size - 1); + index += needle_size - 1; + break; + } + } + } + } + if (!token_it_inc_non_whitespace(&it)){ + break; + } + } +} + +function Range_i64_Array +get_enclosure_ranges(Application_Links *app, Arena *arena, Buffer_ID buffer, i64 pos, u32 flags){ + Range_i64_Array array = {}; + i32 max = 100; + array.ranges = push_array(arena, Range_i64, max); + for (;;){ + Range_i64 range = {}; + if (find_surrounding_nest(app, buffer, pos, flags, &range)){ + array.ranges[array.count] = range; + array.count += 1; + pos = range.first; + if (array.count >= max){ + break; + } + } + else{ + break; + } + } + return(array); +} + +function void +draw_enclosures(Application_Links *app, Text_Layout_ID text_layout_id, Buffer_ID buffer, + i64 pos, u32 flags, Range_Highlight_Kind kind, + ARGB_Color *back_colors, i32 back_count, + ARGB_Color *fore_colors, i32 fore_count){ + Scratch_Block scratch(app); + Range_i64_Array ranges = get_enclosure_ranges(app, scratch, buffer, pos, flags); + + i32 color_index = 0; + for (i32 i = ranges.count - 1; i >= 0; i -= 1){ + Range_i64 range = ranges.ranges[i]; + if (kind == RangeHighlightKind_LineHighlight){ + Range_i64 r[2] = {}; + if (i > 0){ + Range_i64 inner_range = ranges.ranges[i - 1]; + Range_i64 lines = get_line_range_from_pos_range(app, buffer, range); + Range_i64 inner_lines = get_line_range_from_pos_range(app, buffer, inner_range); + inner_lines.min = clamp_bot(lines.min, inner_lines.min); + inner_lines.max = clamp_top(inner_lines.max, lines.max); + inner_lines.min -= 1; + inner_lines.max += 1; + if (lines.min <= inner_lines.min){ + r[0] = Ii64(lines.min, inner_lines.min); + } + if (inner_lines.max <= lines.max){ + r[1] = Ii64(inner_lines.max, lines.max); + } + } + else{ + r[0] = get_line_range_from_pos_range(app, buffer, range); + } + for (i32 j = 0; j < 2; j += 1){ + if (r[j].min == 0){ + continue; + } + Range_i64 line_range = r[j]; + if (back_colors != 0){ + i32 back_index = color_index%back_count; + draw_line_highlight(app, text_layout_id, line_range, back_colors[back_index]); + } + if (fore_colors != 0){ + i32 fore_index = color_index%fore_count; + Range_i64 pos_range = get_pos_range_from_line_range(app, buffer, line_range); + paint_text_color(app, text_layout_id, pos_range, fore_colors[fore_index]); + } + } + } + else{ + if (back_colors != 0){ + i32 back_index = color_index%back_count; + draw_character_block(app, text_layout_id, range.min, 0.f, back_colors[back_index]); + draw_character_block(app, text_layout_id, range.max - 1, 0.f, back_colors[back_index]); + } + if (fore_colors != 0){ + i32 fore_index = color_index%fore_count; + paint_text_color_pos(app, text_layout_id, range.min, fore_colors[fore_index]); + paint_text_color_pos(app, text_layout_id, range.max - 1, fore_colors[fore_index]); + } + } + color_index += 1; + } +} + +function void +draw_scope_highlight(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, + i64 pos, ARGB_Color *colors, i32 color_count){ + draw_enclosures(app, text_layout_id, buffer, + pos, FindNest_Scope, RangeHighlightKind_LineHighlight, + colors, color_count, 0, 0); +} + +function void +draw_paren_highlight(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, + i64 pos, ARGB_Color *colors, i32 color_count){ + Token_Array token_array = get_token_array_from_buffer(app, buffer); + if (token_array.tokens != 0){ + Token_Iterator_Array it = token_iterator_pos(0, &token_array, pos); + Token *token = token_it_read(&it); + if (token != 0 && token->kind == TokenBaseKind_ParentheticalOpen){ + pos = token->pos + token->size; + } + else{ + if (token_it_dec_all(&it)){ + token = token_it_read(&it); + if (token->kind == TokenBaseKind_ParentheticalClose && + pos == token->pos + token->size){ + pos = token->pos; + } + } + } + } + draw_enclosures(app, text_layout_id, buffer, + pos, FindNest_Paren, RangeHighlightKind_CharacterHighlight, + 0, 0, colors, color_count); +} + +function void +draw_jump_highlights(Application_Links *app, Buffer_ID buffer, Text_Layout_ID text_layout_id, + Buffer_ID jump_buffer, FColor line_color){ + Scratch_Block scratch(app); + if (jump_buffer != 0){ + Managed_Scope scopes[2]; + scopes[0] = buffer_get_managed_scope(app, jump_buffer); + scopes[1] = buffer_get_managed_scope(app, buffer); + Managed_Scope comp_scope = get_managed_scope_with_multiple_dependencies(app, scopes, ArrayCount(scopes)); + Managed_Object *markers_object = scope_attachment(app, comp_scope, sticky_jump_marker_handle, Managed_Object); + + i32 count = managed_object_get_item_count(app, *markers_object); + Marker *markers = push_array(scratch, Marker, count); + managed_object_load_data(app, *markers_object, 0, count, markers); + for (i32 i = 0; i < count; i += 1){ + i64 line_number = get_line_number_from_pos(app, buffer, markers[i].pos); + draw_line_highlight(app, text_layout_id, line_number, line_color); + } + } +} + +function b32 +draw_highlight_range(Application_Links *app, View_ID view_id, + Buffer_ID buffer, Text_Layout_ID text_layout_id, + f32 roundness){ + b32 has_highlight_range = false; + Managed_Scope scope = view_get_managed_scope(app, view_id); + Buffer_ID *highlight_buffer = scope_attachment(app, scope, view_highlight_buffer, Buffer_ID); + if (*highlight_buffer != 0){ + if (*highlight_buffer != buffer){ + view_disable_highlight_range(app, view_id); + } + else{ + has_highlight_range = true; + Managed_Object *highlight = scope_attachment(app, scope, view_highlight_range, Managed_Object); + Marker marker_range[2]; + if (managed_object_load_data(app, *highlight, 0, 2, marker_range)){ + Range_i64 range = Ii64(marker_range[0].pos, marker_range[1].pos); + draw_character_block(app, text_layout_id, range, roundness, + fcolor_id(defcolor_highlight)); + paint_text_color_fcolor(app, text_layout_id, range, + fcolor_id(defcolor_at_highlight)); + } + } + } + return(has_highlight_range); +} + +function void +draw_original_4coder_style_cursor_mark_highlight(Application_Links *app, View_ID view_id, b32 is_active_view, + Buffer_ID buffer, Text_Layout_ID text_layout_id, + f32 roundness, f32 outline_thickness){ + b32 has_highlight_range = draw_highlight_range(app, view_id, buffer, text_layout_id, roundness); + if (!has_highlight_range){ + i64 cursor_pos = view_get_cursor_pos(app, view_id); + i64 mark_pos = view_get_mark_pos(app, view_id); + if (is_active_view){ + draw_character_block(app, text_layout_id, cursor_pos, roundness, + fcolor_id(defcolor_cursor)); + paint_text_color_pos(app, text_layout_id, cursor_pos, + fcolor_id(defcolor_at_cursor)); + draw_character_wire_frame(app, text_layout_id, mark_pos, + roundness, outline_thickness, + fcolor_id(defcolor_mark)); + } + else{ + draw_character_wire_frame(app, text_layout_id, mark_pos, + roundness, outline_thickness, + fcolor_id(defcolor_mark)); + draw_character_wire_frame(app, text_layout_id, cursor_pos, + roundness, outline_thickness, + fcolor_id(defcolor_cursor)); + } + } +} + +function void +draw_notepad_style_cursor_highlight(Application_Links *app, View_ID view_id, + Buffer_ID buffer, Text_Layout_ID text_layout_id, + f32 roundness){ + b32 has_highlight_range = draw_highlight_range(app, view_id, buffer, text_layout_id, roundness); + if (!has_highlight_range){ + i64 cursor_pos = view_get_cursor_pos(app, view_id); + i64 mark_pos = view_get_mark_pos(app, view_id); + if (cursor_pos != mark_pos){ + Range_i64 range = Ii64(cursor_pos, mark_pos); + draw_character_block(app, text_layout_id, range, roundness, + fcolor_id(defcolor_highlight)); + paint_text_color_fcolor(app, text_layout_id, range, + fcolor_id(defcolor_at_highlight)); + } + draw_character_i_bar(app, text_layout_id, cursor_pos, fcolor_id(defcolor_cursor)); + } +} + +//////////////////////////////// + +function Rect_f32 +get_contained_box_near_point(Rect_f32 container, Vec2_f32 p, Vec2_f32 box_dims){ + Vec2_f32 container_dims = rect_dim(container); + box_dims.x = clamp_top(box_dims.x, container_dims.x); + box_dims.y = clamp_top(box_dims.y, container_dims.y); + Vec2_f32 q = p + V2f32(-20.f, 22.f); + if (q.x + box_dims.x > container.x1){ + q.x = container.x1 - box_dims.x; + } + if (q.y + box_dims.y > container.y1){ + q.y = p.y - box_dims.y - 2.f; + if (q.y < container.y0){ + q.y = (container.y0 + container.y1 - box_dims.y)*0.5f; + } + } + return(Rf32_xy_wh(q, box_dims)); +} + +function Rect_f32 +draw_tool_tip(Application_Links *app, Face_ID face, Fancy_Block *block, + Vec2_f32 p, Rect_f32 region, f32 x_padding, f32 x_half_padding, + FColor back_color){ + Rect_f32 box = Rf32(p, p); + if (block->line_count > 0){ + Vec2_f32 dims = get_fancy_block_dim(app, face, block); + dims += V2f32(x_padding, 2.f); + box = get_contained_box_near_point(region, p, dims); + box.x0 = f32_round32(box.x0); + box.y0 = f32_round32(box.y0); + box.x1 = f32_round32(box.x1); + box.y1 = f32_round32(box.y1); + Rect_f32 prev_clip = draw_set_clip(app, box); + draw_rectangle_fcolor(app, box, 6.f, back_color); + draw_fancy_block(app, face, fcolor_zero(), block, + box.p0 + V2f32(x_half_padding, 1.f)); + draw_set_clip(app, prev_clip); + } + return(box); +} + +function Rect_f32 +draw_drop_down(Application_Links *app, Face_ID face, Fancy_Block *block, + Vec2_f32 p, Rect_f32 region, f32 x_padding, f32 x_half_padding, + FColor outline_color, FColor back_color){ + Rect_f32 box = Rf32(p, p); + if (block->line_count > 0){ + Vec2_f32 dims = get_fancy_block_dim(app, face, block); + dims += V2f32(x_padding, 4.f); + box = get_contained_box_near_point(region, p, dims); + box.x0 = f32_round32(box.x0); + box.y0 = f32_round32(box.y0); + box.x1 = f32_round32(box.x1); + box.y1 = f32_round32(box.y1); + Rect_f32 prev_clip = draw_set_clip(app, box); + draw_rectangle_fcolor(app, box, 0.f, back_color); + draw_margin(app, box, rect_inner(box, 1.f), outline_color); + draw_fancy_block(app, face, fcolor_zero(), block, + box.p0 + V2f32(x_half_padding, 2.f)); + draw_set_clip(app, prev_clip); + } + return(box); +} + +function b32 +draw_button(Application_Links *app, Rect_f32 rect, Vec2_f32 mouse_p, Face_ID face, String_Const_u8 text){ + b32 hovered = false; + if (rect_contains_point(rect, mouse_p)){ + hovered = true; + } + + FColor margin_color = get_margin_color(hovered?UIHighlight_Active:UIHighlight_None); + draw_rectangle_fcolor(app, rect, 3.f, margin_color); + rect = rect_inner(rect, 3.f); + draw_rectangle_fcolor(app, rect, 3.f, fcolor_id(defcolor_back)); + + Scratch_Block scratch(app); + Fancy_String *fancy = push_fancy_string(scratch, 0, face, fcolor_id(defcolor_text_default), text); + Vec2_f32 dim = get_fancy_string_dim(app, 0, fancy); + Vec2_f32 p = (rect.p0 + rect.p1 - dim)*0.5f; + draw_fancy_string(app, fancy, p); + + return(hovered); +} + +// BOTTOM + diff --git a/custom/4coder_malloc_allocator.cpp b/custom/4coder_malloc_allocator.cpp index a16b7419..8a142f86 100644 --- a/custom/4coder_malloc_allocator.cpp +++ b/custom/4coder_malloc_allocator.cpp @@ -5,7 +5,10 @@ // TOP #include -#include + +#if !OS_MAC +# include +#endif internal void* base_reserve__malloc(void *user_data, u64 size, u64 *size_out, String_Const_u8 location){ diff --git a/custom/4coder_types.h b/custom/4coder_types.h index f432316f..12d365ff 100644 --- a/custom/4coder_types.h +++ b/custom/4coder_types.h @@ -54,7 +54,7 @@ struct Color_Array{ api(custom) struct Color_Table{ - Color_Array *arrays; + Color_Array *arrays; u32 count; }; @@ -741,7 +741,7 @@ struct View_Context{ u64 delta_rule_memory_size; b32 hides_buffer; struct Mapping *mapping; - i64 map_id; + i64 map_id; }; api(custom) @@ -777,4 +777,3 @@ struct Process_State{ }; #endif - diff --git a/custom/bin/buildsuper_x64.sh b/custom/bin/buildsuper_x64.sh index 6b49d4ab..2c83d6b1 100755 --- a/custom/bin/buildsuper_x64.sh +++ b/custom/bin/buildsuper_x64.sh @@ -14,7 +14,8 @@ if [ -z "$SOURCE" ]; then SOURCE="$(readlink -f "$CODE_HOME/4coder_default_bindings.cpp")" fi -opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -Wno-writable-strings -g" +# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" arch=-m64 preproc_file=4coder_command_metadata.i diff --git a/custom/bin/buildsuper_x86.sh b/custom/bin/buildsuper_x86.sh index 46d0ab6e..e3dde9b0 100755 --- a/custom/bin/buildsuper_x86.sh +++ b/custom/bin/buildsuper_x86.sh @@ -35,7 +35,8 @@ done PHYS_DIR=`pwd -P` SOURCE=$PHYS_DIR/$TARGET_FILE -opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -Wno-writable-strings -g" +# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" arch=-m32 cd "$REAL_PWD" diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index db1e23b1..a6e993c2 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -251,235 +251,235 @@ i32 source_name_len; i32 line_number; }; static Command_Metadata fcoder_metacmd_table[229] = { -{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 409 }, -{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "w:\\4ed\\code\\custom\\4coder_auto_indent.cpp", 41, 375 }, -{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "w:\\4ed\\code\\custom\\4coder_auto_indent.cpp", 41, 385 }, -{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "w:\\4ed\\code\\custom\\4coder_auto_indent.cpp", 41, 366 }, -{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 154 }, -{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 96 }, -{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 613 }, -{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "w:\\4ed\\code\\custom\\4coder_build_commands.cpp", 44, 165 }, -{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "w:\\4ed\\code\\custom\\4coder_build_commands.cpp", 44, 128 }, -{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 197 }, -{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 284 }, -{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 290 }, -{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "w:\\4ed\\code\\custom\\4coder_build_commands.cpp", 44, 186 }, -{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 578 }, -{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 480 }, -{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 233 }, -{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 223 }, -{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 243 }, -{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 255 }, -{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 842 }, -{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "w:\\4ed\\code\\custom\\4coder_build_commands.cpp", 44, 180 }, -{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 621 }, -{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "w:\\4ed\\code\\custom\\4coder_docs.cpp", 34, 190 }, -{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 668 }, -{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 125 }, -{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 149 }, -{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 19 }, -{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 124 }, -{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "w:\\4ed\\code\\custom\\4coder_docs.cpp", 34, 175 }, -{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 28 }, -{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 684 }, -{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1798 }, -{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "w:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 7 }, -{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "w:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 23 }, -{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "w:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 57 }, -{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 162 }, -{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 79 }, -{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 112 }, -{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1221 }, -{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1396 }, -{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 134 }, -{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1382 }, -{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "w:\\4ed\\code\\custom\\4coder_cli_command.cpp", 41, 22 }, -{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "w:\\4ed\\code\\custom\\4coder_cli_command.cpp", 41, 7 }, -{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 740 }, -{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2184 }, -{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2192 }, -{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 523 }, -{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 540 }, -{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 346 }, -{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 373 }, -{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 748 }, -{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 462 }, -{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 492 }, -{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 479 }, -{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 509 }, -{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 651 }, -{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 637 }, -{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "w:\\4ed\\code\\custom\\4coder_tutorial.cpp", 38, 869 }, -{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 70 }, -{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 562 }, -{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "w:\\4ed\\code\\custom\\4coder_jump_sticky.cpp", 41, 579 }, -{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 673 }, -{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 515 }, -{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 597 }, -{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 634 }, -{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 563 }, -{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 505 }, -{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "w:\\4ed\\code\\custom\\4coder_code_index_listers.cpp", 48, 12 }, -{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "w:\\4ed\\code\\custom\\4coder_keyboard_macro.cpp", 44, 57 }, -{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "w:\\4ed\\code\\custom\\4coder_keyboard_macro.cpp", 44, 80 }, -{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "w:\\4ed\\code\\custom\\4coder_keyboard_macro.cpp", 44, 44 }, -{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1542 }, -{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "w:\\4ed\\code\\custom\\4coder_tutorial.cpp", 38, 9 }, -{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 211 }, -{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "w:\\4ed\\code\\custom\\4coder_function_list.cpp", 43, 295 }, -{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "w:\\4ed\\code\\custom\\4coder_function_list.cpp", 43, 301 }, -{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "w:\\4ed\\code\\custom\\4coder_function_list.cpp", 43, 267 }, -{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "w:\\4ed\\code\\custom\\4coder_function_list.cpp", 43, 277 }, -{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 162 }, -{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 174 }, -{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 186 }, -{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 192 }, -{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 198 }, -{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 204 }, -{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 210 }, -{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 218 }, -{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 168 }, -{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 180 }, -{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 862 }, -{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 457 }, -{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 469 }, -{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1336 }, -{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 44 }, -{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 237 }, -{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 249 }, -{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 29 }, -{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 231 }, -{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 243 }, -{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 695 }, -{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 265 }, -{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 338 }, -{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 350 }, -{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 356 }, -{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 409 }, -{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 433 }, -{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 421 }, -{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 439 }, -{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 516 }, -{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 530 }, -{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 488 }, -{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 473 }, -{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 502 }, -{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1376 }, -{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1370 }, -{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 447 }, -{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 509 }, -{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 523 }, -{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 481 }, -{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 465 }, -{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 495 }, -{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 332 }, -{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 344 }, -{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 403 }, -{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 427 }, -{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 415 }, -{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 848 }, -{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 854 }, -{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1461 }, -{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1792 }, -{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 46 }, -{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 62 }, -{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 54 }, -{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1493 }, -{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 310 }, -{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 300 }, -{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 374 }, -{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 366 }, -{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 39 }, -{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 110 }, -{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 71 }, -{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "w:\\4ed\\code\\custom\\4coder_clipboard.cpp", 39, 117 }, -{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 106 }, -{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "w:\\4ed\\code\\custom\\4coder_profile.cpp", 37, 226 }, -{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "w:\\4ed\\code\\custom\\4coder_profile.cpp", 37, 219 }, -{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "w:\\4ed\\code\\custom\\4coder_profile.cpp", 37, 212 }, -{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "w:\\4ed\\code\\custom\\4coder_profile_inspect.cpp", 45, 886 }, -{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 1289 }, -{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 870 }, -{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 896 }, -{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1149 }, -{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1170 }, -{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1186 }, -{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1631 }, -{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1716 }, -{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1298 }, -{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1560 }, -{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1059 }, -{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1050 }, -{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1041 }, -{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 982 }, -{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 994 }, -{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1550 }, -{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 382 }, -{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1265 }, -{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 976 }, -{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 988 }, -{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2172 }, -{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2160 }, -{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2178 }, -{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "w:\\4ed\\code\\custom\\4coder_helper.cpp", 36, 2166 }, -{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 539 }, -{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 57 }, -{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 66 }, -{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 82 }, -{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 99 }, -{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 27 }, -{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "w:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 39 }, -{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 125 }, -{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 112 }, -{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 86 }, -{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 99 }, -{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 115 }, -{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 427 }, -{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 421 }, -{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 1237 }, -{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 1249 }, -{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 1243 }, -{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "w:\\4ed\\code\\custom\\4coder_project_commands.cpp", 46, 1230 }, -{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 644 }, -{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 630 }, -{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "w:\\4ed\\code\\custom\\4coder_log_parser.cpp", 40, 994 }, -{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 179 }, -{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 187 }, -{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 237 }, -{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 403 }, -{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1518 }, -{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 692 }, -{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 565 }, -{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 552 }, -{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 658 }, -{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 667 }, -{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 451 }, -{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 439 }, -{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 433 }, -{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 721 }, -{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 727 }, -{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 415 }, -{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "w:\\4ed\\code\\custom\\4coder_default_framework.cpp", 47, 445 }, -{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 712 }, -{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "w:\\4ed\\code\\custom\\4coder_code_index.cpp", 40, 1160 }, -{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "w:\\4ed\\code\\custom\\4coder_tutorial.cpp", 38, 20 }, -{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "w:\\4ed\\code\\custom\\4coder_tutorial.cpp", 38, 34 }, -{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 137 }, -{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1618 }, -{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1645 }, -{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1506 }, -{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "w:\\4ed\\code\\custom\\4coder_jump_lister.cpp", 41, 59 }, -{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 392 }, -{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "w:\\4ed\\code\\custom\\4coder_search.cpp", 36, 639 }, -{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 94 }, -{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 82 }, -{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 88 }, -{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 67 }, -{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "w:\\4ed\\code\\custom\\4coder_auto_indent.cpp", 41, 395 }, -{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 59 }, -{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 76 }, -{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 73 }, -{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "w:\\4ed\\code\\custom\\4coder_combined_write_commands.cpp", 53, 100 }, +{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "../code/custom/4coder_default_framework.cpp", 43, 409 }, +{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "../code/custom/4coder_auto_indent.cpp", 37, 375 }, +{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "../code/custom/4coder_auto_indent.cpp", 37, 385 }, +{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "../code/custom/4coder_auto_indent.cpp", 37, 366 }, +{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "../code/custom/4coder_base_commands.cpp", 39, 154 }, +{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "../code/custom/4coder_base_commands.cpp", 39, 96 }, +{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "../code/custom/4coder_base_commands.cpp", 39, 613 }, +{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "../code/custom/4coder_build_commands.cpp", 40, 165 }, +{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "../code/custom/4coder_build_commands.cpp", 40, 128 }, +{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "../code/custom/4coder_base_commands.cpp", 39, 197 }, +{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "../code/custom/4coder_default_framework.cpp", 43, 284 }, +{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "../code/custom/4coder_default_framework.cpp", 43, 290 }, +{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "../code/custom/4coder_build_commands.cpp", 40, 186 }, +{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "../code/custom/4coder_base_commands.cpp", 39, 578 }, +{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "../code/custom/4coder_default_framework.cpp", 43, 480 }, +{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "../code/custom/4coder_base_commands.cpp", 39, 233 }, +{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "../code/custom/4coder_base_commands.cpp", 39, 223 }, +{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "../code/custom/4coder_base_commands.cpp", 39, 243 }, +{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "../code/custom/4coder_base_commands.cpp", 39, 255 }, +{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "../code/custom/4coder_project_commands.cpp", 42, 842 }, +{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "../code/custom/4coder_build_commands.cpp", 40, 180 }, +{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "../code/custom/4coder_base_commands.cpp", 39, 621 }, +{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "../code/custom/4coder_docs.cpp", 30, 190 }, +{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "../code/custom/4coder_lists.cpp", 31, 668 }, +{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "../code/custom/4coder_combined_write_commands.cpp", 49, 125 }, +{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "../code/custom/4coder_combined_write_commands.cpp", 49, 149 }, +{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "../code/custom/4coder_clipboard.cpp", 35, 19 }, +{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "../code/custom/4coder_base_commands.cpp", 39, 124 }, +{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "../code/custom/4coder_docs.cpp", 30, 175 }, +{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "../code/custom/4coder_clipboard.cpp", 35, 28 }, +{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "../code/custom/4coder_base_commands.cpp", 39, 684 }, +{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "../code/custom/4coder_base_commands.cpp", 39, 1798 }, +{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "../code/custom/4coder_default_hooks.cpp", 39, 7 }, +{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "../code/custom/4coder_default_hooks.cpp", 39, 23 }, +{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "../code/custom/4coder_default_hooks.cpp", 39, 57 }, +{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "../code/custom/4coder_base_commands.cpp", 39, 162 }, +{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "../code/custom/4coder_base_commands.cpp", 39, 79 }, +{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "../code/custom/4coder_scope_commands.cpp", 40, 112 }, +{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "../code/custom/4coder_base_commands.cpp", 39, 1221 }, +{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "../code/custom/4coder_base_commands.cpp", 39, 1396 }, +{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "../code/custom/4coder_base_commands.cpp", 39, 134 }, +{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "../code/custom/4coder_base_commands.cpp", 39, 1382 }, +{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "../code/custom/4coder_cli_command.cpp", 37, 22 }, +{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "../code/custom/4coder_cli_command.cpp", 37, 7 }, +{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "../code/custom/4coder_base_commands.cpp", 39, 740 }, +{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "../code/custom/4coder_helper.cpp", 32, 2184 }, +{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "../code/custom/4coder_helper.cpp", 32, 2192 }, +{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "../code/custom/4coder_jump_sticky.cpp", 37, 523 }, +{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "../code/custom/4coder_jump_sticky.cpp", 37, 540 }, +{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "../code/custom/4coder_jump_sticky.cpp", 37, 346 }, +{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "../code/custom/4coder_jump_sticky.cpp", 37, 373 }, +{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "../code/custom/4coder_base_commands.cpp", 39, 748 }, +{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "../code/custom/4coder_jump_sticky.cpp", 37, 462 }, +{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "../code/custom/4coder_jump_sticky.cpp", 37, 492 }, +{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "../code/custom/4coder_jump_sticky.cpp", 37, 479 }, +{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "../code/custom/4coder_jump_sticky.cpp", 37, 509 }, +{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "../code/custom/4coder_base_commands.cpp", 39, 651 }, +{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "../code/custom/4coder_base_commands.cpp", 39, 637 }, +{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "../code/custom/4coder_tutorial.cpp", 34, 869 }, +{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "../code/custom/4coder_combined_write_commands.cpp", 49, 70 }, +{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "../code/custom/4coder_jump_sticky.cpp", 37, 562 }, +{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "../code/custom/4coder_jump_sticky.cpp", 37, 579 }, +{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "../code/custom/4coder_base_commands.cpp", 39, 673 }, +{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "../code/custom/4coder_lists.cpp", 31, 515 }, +{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "../code/custom/4coder_lists.cpp", 31, 597 }, +{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "../code/custom/4coder_lists.cpp", 31, 634 }, +{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "../code/custom/4coder_lists.cpp", 31, 563 }, +{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "../code/custom/4coder_lists.cpp", 31, 505 }, +{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "../code/custom/4coder_code_index_listers.cpp", 44, 12 }, +{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "../code/custom/4coder_keyboard_macro.cpp", 40, 57 }, +{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "../code/custom/4coder_keyboard_macro.cpp", 40, 80 }, +{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "../code/custom/4coder_keyboard_macro.cpp", 40, 44 }, +{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "../code/custom/4coder_base_commands.cpp", 39, 1542 }, +{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "../code/custom/4coder_tutorial.cpp", 34, 9 }, +{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "../code/custom/4coder_base_commands.cpp", 39, 211 }, +{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "../code/custom/4coder_function_list.cpp", 39, 295 }, +{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "../code/custom/4coder_function_list.cpp", 39, 301 }, +{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "../code/custom/4coder_function_list.cpp", 39, 267 }, +{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "../code/custom/4coder_function_list.cpp", 39, 277 }, +{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "../code/custom/4coder_search.cpp", 32, 162 }, +{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "../code/custom/4coder_search.cpp", 32, 174 }, +{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "../code/custom/4coder_search.cpp", 32, 186 }, +{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "../code/custom/4coder_search.cpp", 32, 192 }, +{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "../code/custom/4coder_search.cpp", 32, 198 }, +{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "../code/custom/4coder_search.cpp", 32, 204 }, +{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "../code/custom/4coder_search.cpp", 32, 210 }, +{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "../code/custom/4coder_search.cpp", 32, 218 }, +{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "../code/custom/4coder_search.cpp", 32, 168 }, +{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "../code/custom/4coder_search.cpp", 32, 180 }, +{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "../code/custom/4coder_project_commands.cpp", 42, 862 }, +{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "../code/custom/4coder_default_framework.cpp", 43, 457 }, +{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "../code/custom/4coder_default_framework.cpp", 43, 469 }, +{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "../code/custom/4coder_base_commands.cpp", 39, 1336 }, +{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "../code/custom/4coder_miblo_numbers.cpp", 39, 44 }, +{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 237 }, +{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 249 }, +{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "../code/custom/4coder_miblo_numbers.cpp", 39, 29 }, +{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 231 }, +{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 243 }, +{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "../code/custom/4coder_base_commands.cpp", 39, 695 }, +{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "../code/custom/4coder_base_commands.cpp", 39, 265 }, +{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "../code/custom/4coder_base_commands.cpp", 39, 338 }, +{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "../code/custom/4coder_base_commands.cpp", 39, 350 }, +{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "../code/custom/4coder_base_commands.cpp", 39, 356 }, +{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "../code/custom/4coder_base_commands.cpp", 39, 409 }, +{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "../code/custom/4coder_base_commands.cpp", 39, 433 }, +{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "../code/custom/4coder_base_commands.cpp", 39, 421 }, +{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "../code/custom/4coder_base_commands.cpp", 39, 439 }, +{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "../code/custom/4coder_base_commands.cpp", 39, 516 }, +{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "../code/custom/4coder_base_commands.cpp", 39, 530 }, +{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "../code/custom/4coder_base_commands.cpp", 39, 488 }, +{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "../code/custom/4coder_base_commands.cpp", 39, 473 }, +{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "../code/custom/4coder_base_commands.cpp", 39, 502 }, +{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "../code/custom/4coder_base_commands.cpp", 39, 1376 }, +{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "../code/custom/4coder_base_commands.cpp", 39, 1370 }, +{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "../code/custom/4coder_base_commands.cpp", 39, 447 }, +{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "../code/custom/4coder_base_commands.cpp", 39, 509 }, +{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "../code/custom/4coder_base_commands.cpp", 39, 523 }, +{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "../code/custom/4coder_base_commands.cpp", 39, 481 }, +{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "../code/custom/4coder_base_commands.cpp", 39, 465 }, +{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "../code/custom/4coder_base_commands.cpp", 39, 495 }, +{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "../code/custom/4coder_base_commands.cpp", 39, 332 }, +{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "../code/custom/4coder_base_commands.cpp", 39, 344 }, +{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "../code/custom/4coder_base_commands.cpp", 39, 403 }, +{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "../code/custom/4coder_base_commands.cpp", 39, 427 }, +{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "../code/custom/4coder_base_commands.cpp", 39, 415 }, +{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "../code/custom/4coder_project_commands.cpp", 42, 848 }, +{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "../code/custom/4coder_project_commands.cpp", 42, 854 }, +{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "../code/custom/4coder_base_commands.cpp", 39, 1461 }, +{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "../code/custom/4coder_base_commands.cpp", 39, 1792 }, +{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "../code/custom/4coder_combined_write_commands.cpp", 49, 46 }, +{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "../code/custom/4coder_combined_write_commands.cpp", 49, 62 }, +{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "../code/custom/4coder_combined_write_commands.cpp", 49, 54 }, +{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "../code/custom/4coder_base_commands.cpp", 39, 1493 }, +{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "../code/custom/4coder_default_framework.cpp", 43, 310 }, +{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "../code/custom/4coder_default_framework.cpp", 43, 300 }, +{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "../code/custom/4coder_base_commands.cpp", 39, 374 }, +{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "../code/custom/4coder_base_commands.cpp", 39, 366 }, +{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "../code/custom/4coder_clipboard.cpp", 35, 39 }, +{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "../code/custom/4coder_clipboard.cpp", 35, 110 }, +{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "../code/custom/4coder_clipboard.cpp", 35, 71 }, +{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "../code/custom/4coder_clipboard.cpp", 35, 117 }, +{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "../code/custom/4coder_scope_commands.cpp", 40, 106 }, +{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "../code/custom/4coder_profile.cpp", 33, 226 }, +{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "../code/custom/4coder_profile.cpp", 33, 219 }, +{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "../code/custom/4coder_profile.cpp", 33, 212 }, +{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "../code/custom/4coder_profile_inspect.cpp", 41, 886 }, +{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "../code/custom/4coder_project_commands.cpp", 42, 1289 }, +{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "../code/custom/4coder_project_commands.cpp", 42, 870 }, +{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "../code/custom/4coder_project_commands.cpp", 42, 896 }, +{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "../code/custom/4coder_base_commands.cpp", 39, 1149 }, +{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "../code/custom/4coder_base_commands.cpp", 39, 1170 }, +{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "../code/custom/4coder_base_commands.cpp", 39, 1186 }, +{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "../code/custom/4coder_base_commands.cpp", 39, 1631 }, +{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "../code/custom/4coder_base_commands.cpp", 39, 1716 }, +{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "../code/custom/4coder_base_commands.cpp", 39, 1298 }, +{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "../code/custom/4coder_base_commands.cpp", 39, 1560 }, +{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "../code/custom/4coder_base_commands.cpp", 39, 1059 }, +{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "../code/custom/4coder_base_commands.cpp", 39, 1050 }, +{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "../code/custom/4coder_base_commands.cpp", 39, 1041 }, +{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "../code/custom/4coder_base_commands.cpp", 39, 982 }, +{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "../code/custom/4coder_base_commands.cpp", 39, 994 }, +{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "../code/custom/4coder_base_commands.cpp", 39, 1550 }, +{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "../code/custom/4coder_default_framework.cpp", 43, 382 }, +{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "../code/custom/4coder_base_commands.cpp", 39, 1265 }, +{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "../code/custom/4coder_base_commands.cpp", 39, 976 }, +{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "../code/custom/4coder_base_commands.cpp", 39, 988 }, +{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "../code/custom/4coder_helper.cpp", 32, 2172 }, +{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "../code/custom/4coder_helper.cpp", 32, 2160 }, +{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "../code/custom/4coder_helper.cpp", 32, 2178 }, +{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "../code/custom/4coder_helper.cpp", 32, 2166 }, +{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "../code/custom/4coder_base_commands.cpp", 39, 539 }, +{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "../code/custom/4coder_scope_commands.cpp", 40, 57 }, +{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "../code/custom/4coder_scope_commands.cpp", 40, 66 }, +{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "../code/custom/4coder_scope_commands.cpp", 40, 82 }, +{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "../code/custom/4coder_scope_commands.cpp", 40, 99 }, +{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "../code/custom/4coder_scope_commands.cpp", 40, 27 }, +{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "../code/custom/4coder_scope_commands.cpp", 40, 39 }, +{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "../code/custom/4coder_eol.cpp", 29, 125 }, +{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "../code/custom/4coder_eol.cpp", 29, 112 }, +{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "../code/custom/4coder_eol.cpp", 29, 86 }, +{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "../code/custom/4coder_eol.cpp", 29, 99 }, +{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "../code/custom/4coder_base_commands.cpp", 39, 115 }, +{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "../code/custom/4coder_default_framework.cpp", 43, 427 }, +{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "../code/custom/4coder_default_framework.cpp", 43, 421 }, +{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1237 }, +{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1249 }, +{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1243 }, +{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "../code/custom/4coder_project_commands.cpp", 42, 1230 }, +{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "../code/custom/4coder_base_commands.cpp", 39, 644 }, +{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "../code/custom/4coder_base_commands.cpp", 39, 630 }, +{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "../code/custom/4coder_log_parser.cpp", 36, 994 }, +{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "../code/custom/4coder_base_commands.cpp", 39, 179 }, +{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "../code/custom/4coder_base_commands.cpp", 39, 187 }, +{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "../code/custom/4coder_combined_write_commands.cpp", 49, 237 }, +{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "../code/custom/4coder_default_framework.cpp", 43, 403 }, +{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "../code/custom/4coder_base_commands.cpp", 39, 1518 }, +{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "../code/custom/4coder_lists.cpp", 31, 692 }, +{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "../code/custom/4coder_base_commands.cpp", 39, 565 }, +{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "../code/custom/4coder_base_commands.cpp", 39, 552 }, +{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "../code/custom/4coder_base_commands.cpp", 39, 658 }, +{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "../code/custom/4coder_base_commands.cpp", 39, 667 }, +{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "../code/custom/4coder_default_framework.cpp", 43, 451 }, +{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "../code/custom/4coder_default_framework.cpp", 43, 439 }, +{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "../code/custom/4coder_default_framework.cpp", 43, 433 }, +{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "../code/custom/4coder_base_commands.cpp", 39, 721 }, +{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "../code/custom/4coder_base_commands.cpp", 39, 727 }, +{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "../code/custom/4coder_default_framework.cpp", 43, 415 }, +{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "../code/custom/4coder_default_framework.cpp", 43, 445 }, +{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "../code/custom/4coder_base_commands.cpp", 39, 712 }, +{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "../code/custom/4coder_code_index.cpp", 36, 1160 }, +{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "../code/custom/4coder_tutorial.cpp", 34, 20 }, +{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "../code/custom/4coder_tutorial.cpp", 34, 34 }, +{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "../code/custom/4coder_combined_write_commands.cpp", 49, 137 }, +{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "../code/custom/4coder_base_commands.cpp", 39, 1618 }, +{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "../code/custom/4coder_base_commands.cpp", 39, 1645 }, +{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "../code/custom/4coder_base_commands.cpp", 39, 1506 }, +{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "../code/custom/4coder_jump_lister.cpp", 37, 59 }, +{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "../code/custom/4coder_search.cpp", 32, 392 }, +{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "../code/custom/4coder_search.cpp", 32, 639 }, +{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "../code/custom/4coder_combined_write_commands.cpp", 49, 94 }, +{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 82 }, +{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 88 }, +{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "../code/custom/4coder_base_commands.cpp", 39, 67 }, +{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "../code/custom/4coder_auto_indent.cpp", 37, 395 }, +{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "../code/custom/4coder_base_commands.cpp", 39, 59 }, +{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 76 }, +{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "../code/custom/4coder_base_commands.cpp", 39, 73 }, +{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "../code/custom/4coder_combined_write_commands.cpp", 49, 100 }, }; static i32 fcoder_metacmd_ID_allow_mouse = 0; static i32 fcoder_metacmd_ID_auto_indent_line_at_cursor = 1; diff --git a/custom/metadata_generator.dSYM/Contents/Info.plist b/custom/metadata_generator.dSYM/Contents/Info.plist new file mode 100644 index 00000000..a05ad97b --- /dev/null +++ b/custom/metadata_generator.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.metadata_generator + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator b/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator new file mode 100644 index 0000000000000000000000000000000000000000..28c1007f45ecd0a4f588766b5e518e8d7fe6dc50 GIT binary patch literal 486440 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 Yd#&*^$M^S#2(V=a_-h7h($ literal 0 HcmV?d00001 From 67dfd0614911f378d5685442acf6dc50f82f512a Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 23 Dec 2019 02:39:44 +0200 Subject: [PATCH 02/67] Started working on macOS platform layer. --- platform_mac/mac_4ed.mm | 16 ++++++++++++++++ platform_mac/{mac_4ed.cpp => mac_4ed_old.cpp} | 0 platform_mac/{mac_4ed.m => mac_4ed_old.m} | 0 platform_win32/win32_4ed.cpp | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 platform_mac/mac_4ed.mm rename platform_mac/{mac_4ed.cpp => mac_4ed_old.cpp} (100%) rename platform_mac/{mac_4ed.m => mac_4ed_old.m} (100%) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm new file mode 100644 index 00000000..e5993845 --- /dev/null +++ b/platform_mac/mac_4ed.mm @@ -0,0 +1,16 @@ +int +main(int arg_count, char **args){ + Thread_Context _tctx = {}; + thread_ctx_init(&_tctx, ThreadKind_Main, + get_base_allocator_system(), + get_base_allocator_system()); + + block_zero_struct(&global_mac_vars); + global_mac_vars.tctx = &_tctx; + + // NOTE(yuval): Application Core Update + Application_Step_Result result = {}; + if (app.step != 0){ + result = app.step(mac_vars.tctx, &target, base_ptr, &input); + } +} \ No newline at end of file diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed_old.cpp similarity index 100% rename from platform_mac/mac_4ed.cpp rename to platform_mac/mac_4ed_old.cpp diff --git a/platform_mac/mac_4ed.m b/platform_mac/mac_4ed_old.m similarity index 100% rename from platform_mac/mac_4ed.m rename to platform_mac/mac_4ed_old.m diff --git a/platform_win32/win32_4ed.cpp b/platform_win32/win32_4ed.cpp index 0ed48a78..e1fec7a4 100644 --- a/platform_win32/win32_4ed.cpp +++ b/platform_win32/win32_4ed.cpp @@ -335,7 +335,7 @@ system_set_fullscreen_sig(){ internal system_is_fullscreen_sig(){ - // NOTE(allen): Report the fullscreen status as it would be set at the beginning of the + // NOTE(allen): Report the fullscreen status as it would be set at the beginning of the // next frame. That is, take into account all fullscreen toggle requests that have come in // already this frame. Read: "full_screen XOR do_toggle" b32 result = (win32vars.full_screen != win32vars.do_toggle); From 65774dec4651752259f1a26aa5753e9ee9e8bf23 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 23 Dec 2019 20:40:51 +0200 Subject: [PATCH 03/67] Replaced readlink -f with realpath which works for both macOS and Linux. Might need to replace this with an implementation of readlink -f if we find that this doesn't work well enough for both macOS and Linux. --- bin/build.sh | 4 ++-- custom/bin/buildsuper_x64.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/build.sh b/bin/build.sh index 491067b0..90b1fa46 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -4,8 +4,8 @@ set -e # Set up directories (mirrors build.bat) -# NOTE(yuval): Temporary fix that works only for macOS -ME="$(greadlink -f "$0")" +# NOTE(yuval): Replaced readlink with realpath which works for both macOS and Linux +ME="$(realpath "$0")" LOCATION="$(dirname "$ME")" SRC_ROOT="$(dirname "$LOCATION")" PROJECT_ROOT="$(dirname "$SRC_ROOT")" diff --git a/custom/bin/buildsuper_x64.sh b/custom/bin/buildsuper_x64.sh index 2c83d6b1..e04e79cf 100755 --- a/custom/bin/buildsuper_x64.sh +++ b/custom/bin/buildsuper_x64.sh @@ -4,7 +4,7 @@ set -e # Store the real CWD -ME="$(readlink -f "$0")" +ME="$(realpath "$0")" LOCATION="$(dirname "$ME")" CODE_HOME="$(dirname "$LOCATION")" From 921a68e765ca951b14b06c28d57be6c0eacbb259 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 24 Dec 2019 01:22:56 +0200 Subject: [PATCH 04/67] Created separate build files for macOS. --- bin/4ed_build.cpp | 3 +- bin/build-mac.sh | 43 ++++++++++++++++++++++++++ bin/build.sh | 5 ++- bin/build_x86-mac.sh | 5 +++ custom/bin/buildsuper_x64-mac.sh | 30 ++++++++++++++++++ custom/bin/buildsuper_x64.sh | 5 ++- custom/bin/buildsuper_x86-mac.sh | 53 ++++++++++++++++++++++++++++++++ custom/bin/buildsuper_x86.sh | 3 +- platform_mac/mac_4ed.mm | 39 +++++++++++++++-------- platform_mac/mac_4ed_old.m | 16 ++++++---- 10 files changed, 175 insertions(+), 27 deletions(-) create mode 100755 bin/build-mac.sh create mode 100644 bin/build_x86-mac.sh create mode 100755 custom/bin/buildsuper_x64-mac.sh create mode 100755 custom/bin/buildsuper_x86-mac.sh diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 235e2813..90a1e350 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -90,7 +90,7 @@ char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed.m", "platform_mac/mac_4ed.cpp", 0 }; +char *mac_platform_layer[] = { "platform_mac/mac_4ed_old.m", 0 }; char **platform_layers[Platform_COUNT] = { windows_platform_layer, @@ -387,6 +387,7 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha fm_finish_build_line(&line); Temp_Dir temp = fm_pushdir(out_path); + printf("Build: g++ %s -o %s\n", line.build_options, out_file); systemf("g++ %s -o %s", line.build_options, out_file); fm_popdir(temp); } diff --git a/bin/build-mac.sh b/bin/build-mac.sh new file mode 100755 index 00000000..9d77d2e9 --- /dev/null +++ b/bin/build-mac.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# If any command errors, stop the script +set -e + +# Set up directories (mirrors build.bat) +# NOTE(yuval): Replaced readlink with realpath which works for both macOS and Linux +ME="$(realpath "$0")" +LOCATION="$(dirname "$ME")" +SRC_ROOT="$(dirname "$LOCATION")" +PROJECT_ROOT="$(dirname "$SRC_ROOT")" +if [ ! -d "$PROJECT_ROOT/build" ]; then +mkdir "$PROJECT_ROOT/build" +fi +BUILD_ROOT="$PROJECT_ROOT/build" +BIN_ROOT="$SRC_ROOT/bin" +CUSTOM_ROOT="$SRC_ROOT/custom" +CUSTOM_BIN="$CUSTOM_ROOT/bin" + +# Get the build mode +BUILD_MODE="$1" +if [ -z "$BUILD_MODE" ]; then + BUILD_MODE="-DDEV_BUILD" +fi + +# Get the OS specific flags +chmod +rx "$BIN_ROOT/detect_os.sh" +os=$("$BIN_ROOT/detect_os.sh") + +if [[ "$os" == "linux" ]]; then +WARNINGS="-Wno-write-strings -Wno-comment" +elif [[ "$os" == "mac" ]]; then +WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-switch" +fi + +FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" +INCLUDES="-I$SRC_ROOT -I$CUSTOM_ROOT" + +# Execute +clang++ $WARNINGS $FLAGS $INCLUDES "$BIN_ROOT/4ed_build.cpp" -g -o "$BUILD_ROOT/build" +pushd "$SRC_ROOT" +"$BUILD_ROOT/build" +popd diff --git a/bin/build.sh b/bin/build.sh index 90b1fa46..0d01ab2d 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -4,8 +4,7 @@ set -e # Set up directories (mirrors build.bat) -# NOTE(yuval): Replaced readlink with realpath which works for both macOS and Linux -ME="$(realpath "$0")" +ME="$(readlink -f "$0")" LOCATION="$(dirname "$ME")" SRC_ROOT="$(dirname "$LOCATION")" PROJECT_ROOT="$(dirname "$SRC_ROOT")" @@ -30,7 +29,7 @@ os=$("$BIN_ROOT/detect_os.sh") if [[ "$os" == "linux" ]]; then WARNINGS="-Wno-write-strings -Wno-comment" elif [[ "$os" == "mac" ]]; then -WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-switch" +WARNINGS="-Wno-write-strings -Wno-comment -Wno-logical-op-parentheses -Wno-null-dereference -Wno-switch" fi FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" diff --git a/bin/build_x86-mac.sh b/bin/build_x86-mac.sh new file mode 100644 index 00000000..3642eddf --- /dev/null +++ b/bin/build_x86-mac.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +./build.sh -DDEV_BUILD_X86 + + diff --git a/custom/bin/buildsuper_x64-mac.sh b/custom/bin/buildsuper_x64-mac.sh new file mode 100755 index 00000000..e04e79cf --- /dev/null +++ b/custom/bin/buildsuper_x64-mac.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# If any command errors, stop the script +set -e + +# Store the real CWD +ME="$(realpath "$0")" +LOCATION="$(dirname "$ME")" +CODE_HOME="$(dirname "$LOCATION")" + +# Find the most reasonable candidate build file +SOURCE="$1" +if [ -z "$SOURCE" ]; then + SOURCE="$(readlink -f "$CODE_HOME/4coder_default_bindings.cpp")" +fi + +# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" +arch=-m64 + +preproc_file=4coder_command_metadata.i +meta_macros="-DMETA_PASS" +g++ -I"$CODE_HOME" $meta_macros $arch $opts $debug -std=gnu++0x "$SOURCE" -E -o $preproc_file +g++ -I"$CODE_HOME" $opts $debug -std=gnu++0x "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" +"$CODE_HOME/metadata_generator" -R "$CODE_HOME" "$PWD/$preproc_file" + +g++ -I"$CODE_HOME" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC + +rm "$CODE_HOME/metadata_generator" +rm $preproc_file diff --git a/custom/bin/buildsuper_x64.sh b/custom/bin/buildsuper_x64.sh index e04e79cf..6b49d4ab 100755 --- a/custom/bin/buildsuper_x64.sh +++ b/custom/bin/buildsuper_x64.sh @@ -4,7 +4,7 @@ set -e # Store the real CWD -ME="$(realpath "$0")" +ME="$(readlink -f "$0")" LOCATION="$(dirname "$ME")" CODE_HOME="$(dirname "$LOCATION")" @@ -14,8 +14,7 @@ if [ -z "$SOURCE" ]; then SOURCE="$(readlink -f "$CODE_HOME/4coder_default_bindings.cpp")" fi -# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings -opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -Wno-writable-strings -g" arch=-m64 preproc_file=4coder_command_metadata.i diff --git a/custom/bin/buildsuper_x86-mac.sh b/custom/bin/buildsuper_x86-mac.sh new file mode 100755 index 00000000..e3dde9b0 --- /dev/null +++ b/custom/bin/buildsuper_x86-mac.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Store the real CWD +REAL_PWD="$PWD" + +# Find the code home folder +TARGET_FILE="$0" +cd `dirname $TARGET_FILE` +TARGET_FILE=`basename $TARGET_FILE` +while [ -L "$TARGET_FILE" ] +do + TARGET_FILE=`readlink $TARGET_FILE` + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` +done +PHYS_DIR=`pwd -P` +SCRIPT_FILE=$PHYS_DIR/$TARGET_FILE +code_home=$(dirname "$SCRIPT_FILE") + +# Find the most reasonable candidate build file +SOURCE="$1" +if [ -z "$SOURCE" ]; then + SOURCE="$code_home/4coder_default_bindings.cpp" +fi + +TARGET_FILE="$SOURCE" +cd `dirname $TARGET_FILE` +TARGET_FILE=`basename $TARGET_FILE` +while [ -L "$TARGET_FILE" ] +do + TARGET_FILE=`readlink $TARGET_FILE` + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` +done +PHYS_DIR=`pwd -P` +SOURCE=$PHYS_DIR/$TARGET_FILE + +# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" +arch=-m32 + +cd "$REAL_PWD" +preproc_file=4coder_command_metadata.i +meta_macros="-DMETA_PASS" +g++ -I"$code_home" $meta_macros $arch $opts $debug -std=gnu++0x "$SOURCE" -E -o $preproc_file +g++ -I"$code_home" $opts $debug -std=gnu++0x "$code_home/4coder_metadata_generator.cpp" -o metadata_generator +./metadata_generator -R "$code_home" "$PWD/$preproc_file" + +g++ -I"$code_home" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC + +rm metadata_generator +rm $preproc_file + diff --git a/custom/bin/buildsuper_x86.sh b/custom/bin/buildsuper_x86.sh index e3dde9b0..46d0ab6e 100755 --- a/custom/bin/buildsuper_x86.sh +++ b/custom/bin/buildsuper_x86.sh @@ -35,8 +35,7 @@ done PHYS_DIR=`pwd -P` SOURCE=$PHYS_DIR/$TARGET_FILE -# NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings -opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -Wno-writable-strings -g" arch=-m32 cd "$REAL_PWD" diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index e5993845..8182c122 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,16 +1,31 @@ +// #include + int main(int arg_count, char **args){ - Thread_Context _tctx = {}; - thread_ctx_init(&_tctx, ThreadKind_Main, - get_base_allocator_system(), - get_base_allocator_system()); - - block_zero_struct(&global_mac_vars); - global_mac_vars.tctx = &_tctx; - - // NOTE(yuval): Application Core Update - Application_Step_Result result = {}; - if (app.step != 0){ - result = app.step(mac_vars.tctx, &target, base_ptr, &input); + @autoreleasepool{ + // NOTE(yuval): NSApplication & Delegate Creation + NSApplication* app = [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + App_Delegate* app_delegate = [[App_Delegate alloc] init]; + [app setDelegate:app_delegate]; + + [NSApp finishLaunching]; + +#if 0 + Thread_Context _tctx = {}; + thread_ctx_init(&_tctx, ThreadKind_Main, + get_base_allocator_system(), + get_base_allocator_system()); + + block_zero_struct(&global_mac_vars); + global_mac_vars.tctx = &_tctx; + + // NOTE(yuval): Application Core Update + Application_Step_Result result = {}; + if (app.step != 0){ + result = app.step(mac_vars.tctx, &target, base_ptr, &input); + } +#endif } } \ No newline at end of file diff --git a/platform_mac/mac_4ed_old.m b/platform_mac/mac_4ed_old.m index ebadc1ca..098da2fe 100644 --- a/platform_mac/mac_4ed_old.m +++ b/platform_mac/mac_4ed_old.m @@ -9,6 +9,7 @@ // TOP +#if 0 #define IS_OBJC_LAYER #include "4coder_base_types.h" @@ -23,6 +24,8 @@ #define external #include "osx_objective_c_to_cpp_links.h" +#endif + #include #import @@ -30,7 +33,7 @@ #import #import #import - +#if 0 #include #include #include @@ -814,9 +817,9 @@ osx_list_loadable_fonts(void){ NSString *font_n = fonts[i]; char *font_n_c = (char*)[font_n UTF8String]; NSFont *font = [font_manager - fontWithFamily:font_n - traits:NSUnboldFontMask|NSUnitalicFontMask - weight:5 + fontWithFamily:font_n + traits:NSUnboldFontMask|NSUnitalicFontMask + weight:5 size:12]; NSString *path = get_font_path(font); char *path_c = 0; @@ -840,9 +843,10 @@ OSX_Keyboard_Modifiers osx_get_modifiers(void){ return(osx_mods_nsevent_to_struct([NSEvent modifierFlags])); } - +#endif int main(int argc, char **argv){ +#if 0 memset(&osx_objc, 0, sizeof(osx_objc)); u32 clipboard_size = KB(16); @@ -887,7 +891,7 @@ main(int argc, char **argv){ [NSApp run]; } - +#endif return(0); } From 42f06f0eecda062dc1ed6444e0db332316648b94 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 24 Dec 2019 02:46:58 +0200 Subject: [PATCH 05/67] Added clang compiler support. The macOS platform now uses clang instead of gcc as its compiler. --- 4ed_api_implementation.cpp | 26 +- 4ed_buffer.cpp | 4 +- 4ed_string_matching.cpp | 4 +- bin/4ed_build.cpp | 135 +++++- bin/build-mac.sh | 2 +- custom/4coder_base_types.cpp | 2 +- custom/4coder_base_types.h | 67 ++- custom/4coder_command_map.cpp | 32 +- custom/4coder_file_moving.h | 14 +- custom/bin/buildsuper_x64-mac.sh | 8 +- custom/generated/command_metadata.h | 458 +++++++++--------- .../Resources/DWARF/metadata_generator | Bin 486440 -> 136740 bytes platform_mac/mac_4ed.mm | 2 +- 13 files changed, 430 insertions(+), 324 deletions(-) diff --git a/4ed_api_implementation.cpp b/4ed_api_implementation.cpp index 933262b4..9086beac 100644 --- a/4ed_api_implementation.cpp +++ b/4ed_api_implementation.cpp @@ -453,7 +453,7 @@ buffer_line_y_difference(Application_Links *app, Buffer_ID buffer_id, i64 line_a, i64 line_b){ Models *models = (Models*)app->cmd_context; Editing_File *file = imp_get_file(models, buffer_id); - f32 result = {}; + f32 result = 0.0f; if (api_check_buffer(file)){ Face *face = font_set_face_from_id(&models->font_set, face_id); if (face != 0){ @@ -527,7 +527,7 @@ buffer_relative_character_from_pos(Application_Links *app, Buffer_ID buffer_id, { Models *models = (Models*)app->cmd_context; Editing_File *file = imp_get_file(models, buffer_id); - i64 result = {}; + i64 result = 0; if (api_check_buffer(file)){ Face *face = font_set_face_from_id(&models->font_set, face_id); if (face != 0){ @@ -563,7 +563,7 @@ api(custom) function f32 view_line_y_difference(Application_Links *app, View_ID view_id, i64 line_a, i64 line_b){ Models *models = (Models*)app->cmd_context; View *view = imp_get_view(models, view_id); - f32 result = {}; + f32 result = 0.0f; if (api_check_view(view)){ result = view_line_y_difference(app->tctx, models, view, line_a, line_b); } @@ -607,7 +607,7 @@ api(custom) function i64 view_relative_character_from_pos(Application_Links *app, View_ID view_id, i64 base_line, i64 pos){ Models *models = (Models*)app->cmd_context; View *view = imp_get_view(models, view_id); - i64 result = {}; + i64 result = 0; if (api_check_view(view)){ result = view_relative_character_from_pos(app->tctx, models, view, base_line, pos); } @@ -618,7 +618,7 @@ api(custom) function i64 view_pos_from_relative_character(Application_Links *app, View_ID view_id, i64 base_line, i64 character){ Models *models = (Models*)app->cmd_context; View *view = imp_get_view(models, view_id); - i64 result = {}; + i64 result = 0; if (api_check_view(view)){ result = view_pos_from_relative_character(app->tctx, models, view, base_line, character); } @@ -702,7 +702,7 @@ api(custom) function Dirty_State buffer_get_dirty_state(Application_Links *app, Buffer_ID buffer_id){ Models *models = (Models*)app->cmd_context; Editing_File *file = imp_get_file(models, buffer_id); - Dirty_State result = {}; + Dirty_State result = 0; if (api_check_buffer(file)){ result = file->state.dirty; } @@ -1680,7 +1680,7 @@ view_set_buffer(Application_Links *app, View_ID view_id, Buffer_ID buffer_id, Se // TODO(allen): remove this! api(custom) function b32 -view_post_fade(Application_Links *app, View_ID view_id, f32 seconds, Range_i64 range, +view_post_fade(Application_Links *app, View_ID view_id, f32 seconds, Range_i64 range, ARGB_Color color){ Models *models = (Models*)app->cmd_context; View *view = imp_get_view(models, view_id); @@ -2390,7 +2390,7 @@ set_global_face(Application_Links *app, Face_ID id) b32 result = false; Face *face = font_set_face_from_id(&models->font_set, id); if (face != 0){ - models->global_face_id = face->id; + models->global_face_id = face->id; result = true; } return(result); @@ -2796,7 +2796,7 @@ api(custom) function Text_Layout_ID text_layout_create(Application_Links *app, Buffer_ID buffer_id, Rect_f32 rect, Buffer_Point buffer_point){ Models *models = (Models*)app->cmd_context; Editing_File *file = imp_get_file(models, buffer_id); - Text_Layout_ID result = {}; + Text_Layout_ID result = 0; if (api_check_buffer(file)){ Thread_Context *tctx = app->tctx; Scratch_Block scratch(tctx); @@ -2826,10 +2826,10 @@ text_layout_create(Application_Links *app, Buffer_ID buffer_id, Rect_f32 rect, B Range_i64 visible_line_number_range = Ii64(buffer_point.line_number, line_number); Range_i64 visible_range = Ii64(buffer_get_first_pos_from_line_number(buffer, visible_line_number_range.min), - buffer_get_last_pos_from_line_number(buffer, visible_line_number_range.max)); + buffer_get_last_pos_from_line_number(buffer, visible_line_number_range.max)); i64 item_count = range_size_inclusive(visible_range); - ARGB_Color *colors_array = push_array_zero(arena, ARGB_Color, item_count); + ARGB_Color *colors_array = push_array_zero(arena, ARGB_Color, item_count); result = text_layout_new(&models->text_layouts, arena, buffer_id, buffer_point, visible_range, visible_line_number_range, rect, colors_array, layout_func); @@ -2944,7 +2944,7 @@ text_layout_character_on_screen(Application_Links *app, Text_Layout_ID layout_id y += line.height; } - // TODO(allen): optimization: This is some fairly heavy computation. We really + // TODO(allen): optimization: This is some fairly heavy computation. We really // need to accelerate the (pos -> item) lookup within a single // Buffer_Layout_Item_List. b32 is_first_item = true; @@ -2987,7 +2987,7 @@ paint_text_color(Application_Links *app, Text_Layout_ID layout_id, Range_i64 ran range.max = clamp_top(range.max, layout->visible_range.max); range.min -= layout->visible_range.min; range.max -= layout->visible_range.min; - ARGB_Color *color_ptr = layout->item_colors + range.min; + ARGB_Color *color_ptr = layout->item_colors + range.min; for (i64 i = range.min; i < range.max; i += 1, color_ptr += 1){ *color_ptr = color; } diff --git a/4ed_buffer.cpp b/4ed_buffer.cpp index c625e6f0..c155b9dc 100644 --- a/4ed_buffer.cpp +++ b/4ed_buffer.cpp @@ -705,7 +705,7 @@ buffer_get_pos_range_from_line_number(Gap_Buffer *buffer, i64 line_number){ internal i64 buffer_get_first_pos_from_line_number(Gap_Buffer *buffer, i64 line_number){ - i64 result = {}; + i64 result = 0; if (line_number < 1){ result = 0; } @@ -720,7 +720,7 @@ buffer_get_first_pos_from_line_number(Gap_Buffer *buffer, i64 line_number){ internal i64 buffer_get_last_pos_from_line_number(Gap_Buffer *buffer, i64 line_number){ - i64 result = {}; + i64 result = 0; if (line_number < 1){ result = 0; } diff --git a/4ed_string_matching.cpp b/4ed_string_matching.cpp index 4fd28843..2907d24b 100644 --- a/4ed_string_matching.cpp +++ b/4ed_string_matching.cpp @@ -125,7 +125,7 @@ find_all_matches_forward(Arena *arena, i32 maximum_output_count, jump_back_0: if (n + 1 == needle.size){ - String_Match_Flag flags = {}; + String_Match_Flag flags = 0; if (!(last_insensitive >= 0 && j <= (u64)last_insensitive && (u64)last_insensitive < j + needle.size)){ @@ -245,7 +245,7 @@ find_all_matches_backward(Arena *arena, i32 maximum_output_count, jump_back_0: if (n + 1 == needle.size){ - String_Match_Flag flags = {}; + String_Match_Flag flags = 0; if (!(last_insensitive < size && j >= last_insensitive && last_insensitive > j - (i64)needle.size)){ diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 90a1e350..146169fb 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -43,6 +43,7 @@ char *platform_names[] = { enum{ Compiler_CL, Compiler_GCC, + Compiler_Clang, // Compiler_COUNT, Compiler_None = Compiler_COUNT, @@ -51,6 +52,7 @@ enum{ char *compiler_names[] = { "cl", "gcc", + "clang" }; #if OS_WINDOWS @@ -67,6 +69,8 @@ char *compiler_names[] = { # define This_Compiler Compiler_CL #elif COMPILER_GCC # define This_Compiler Compiler_GCC +#elif COMPILER_CLANG +# define This_Compiler Compiler_Clang #else # error This compilers is not enumerated. #endif @@ -90,7 +94,7 @@ char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed_old.m", 0 }; +char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", 0 }; char **platform_layers[Platform_COUNT] = { windows_platform_layer, @@ -100,12 +104,12 @@ char **platform_layers[Platform_COUNT] = { char *windows_cl_platform_inc[] = { "platform_all", 0 }; char *linux_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; -char *mac_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; +char *mac_clang_platform_inc[] = { "platform_all", "platform_unix", 0 }; char **platform_includes[Platform_COUNT][Compiler_COUNT] = { - {windows_cl_platform_inc, 0 }, - {0 , linux_gcc_platform_inc}, - {0 , mac_gcc_platform_inc }, + {windows_cl_platform_inc, 0 , 0}, + {0 , linux_gcc_platform_inc, 0}, + {0 , 0 , mac_clang_platform_inc}, }; char *default_custom_target = "../code/custom/4coder_default_bindings.cpp"; @@ -298,25 +302,6 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha #define GCC_LIBS_X64 GCC_LIBS_COMMON #define GCC_LIBS_X86 GCC_LIBS_COMMON -#elif OS_MAC - -# define GCC_OPTS \ -"-Wno-write-strings -Wno-deprecated-declarations " \ -"-Wno-comment -Wno-switch -Wno-null-dereference " \ -"-Wno-tautological-compare " \ -"-Wno-unused-result " - -#define GCC_LIBS_COMMON \ -"-framework Cocoa -framework QuartzCore " \ -"-framework CoreServices " \ -"-framework OpenGL -framework IOKit " - -#define GCC_LIBS_X64 GCC_LIBS_COMMON \ -FOREIGN "/x64/libfreetype-mac.a" - -#define GCC_LIBS_X86 GCC_LIBS_COMMON \ -FOREIGN "/x86/libfreetype-mac.a" - #else # error gcc options not set for this platform #endif @@ -387,11 +372,105 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha fm_finish_build_line(&line); Temp_Dir temp = fm_pushdir(out_path); - printf("Build: g++ %s -o %s\n", line.build_options, out_file); systemf("g++ %s -o %s", line.build_options, out_file); fm_popdir(temp); } +#elif COMPILER_CLANG + +#if OS_MAC + +# define CLANG_OPTS \ +"-Wno-write-strings -Wno-deprecated-declarations " \ +"-Wno-comment -Wno-switch -Wno-null-dereference " \ +"-Wno-tautological-compare " \ +"-Wno-unused-result -Wno-missing-declarations -std=c++11 " + +#define CLANG_LIBS_COMMON \ +"-framework Cocoa -framework QuartzCore " \ +"-framework CoreServices " \ +"-framework OpenGL -framework IOKit " + +#define CLANG_LIBS_X64 CLANG_LIBS_COMMON \ +FOREIGN "/x64/libfreetype-mac.a" + +#define CLANG_LIBS_X86 CLANG_LIBS_COMMON \ +FOREIGN "/x86/libfreetype-mac.a" + +#else +# error gcc options not set for this platform +#endif + +internal void +build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ + Build_Line line; + fm_init_build_line(&line); + + switch (arch){ + case Arch_X64: + fm_add_to_line(line, "-m64"); + fm_add_to_line(line, "-DFTECH_64_BIT"); break; + + case Arch_X86: + fm_add_to_line(line, "-m32"); + fm_add_to_line(line, "-DFTECH_32_BIT"); break; + + default: InvalidPath; + } + + if (flags & OPTS){ + fm_add_to_line(line, CLANG_OPTS); + } + + fm_add_to_line(line, "-I%s", code_path); + if (inc_folders != 0){ + for (u32 i = 0; inc_folders[i] != 0; ++i){ + char *str = fm_str(arena, code_path, "/", inc_folders[i]); + fm_add_to_line(line, "-I%s", str); + } + } + + if (flags & DEBUG_INFO){ + fm_add_to_line(line, "-g -O0"); + } + + if (flags & OPTIMIZATION){ + fm_add_to_line(line, "-O3"); + } + + if (flags & SHARED_CODE){ + fm_add_to_line(line, "-shared"); + } + + if (defines != 0){ + for (u32 i = 0; defines[i]; ++i){ + char *define_flag = fm_str(arena, "-D", defines[i]); + fm_add_to_line(line, "%s", define_flag); + } + } + + fm_add_to_line(line, "-I\"%s\"", code_path); + for (u32 i = 0; code_files[i] != 0; ++i){ + fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]); + } + + if (flags & LIBS){ + if (arch == Arch_X64){ + fm_add_to_line(line, CLANG_LIBS_X64); + } + else if (arch == Arch_X86) + { + fm_add_to_line(line, CLANG_LIBS_X86); + } + } + + fm_finish_build_line(&line); + + Temp_Dir temp = fm_pushdir(out_path); + systemf("clang++ %s -o %s", line.build_options, out_file); + fm_popdir(temp); +} + #else # error build function not defined for this compiler #endif @@ -428,7 +507,11 @@ buildsuper(Arena *arena, char *cdir, char *file, u32 arch){ BEGIN_TIME_SECTION(); Temp_Dir temp = fm_pushdir(fm_str(arena, BUILD_DIR)); - char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], BAT); + char *build_script_postfix = ""; + if (This_OS == Platform_Mac){ + build_script_postfix = "-mac"; + } + char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], build_script_postfix, BAT); char *build_command = fm_str(arena, "\"", cdir, "/", build_script, "\" \"", file, "\""); if (This_OS == Platform_Windows){ diff --git a/bin/build-mac.sh b/bin/build-mac.sh index 9d77d2e9..83062702 100755 --- a/bin/build-mac.sh +++ b/bin/build-mac.sh @@ -30,7 +30,7 @@ os=$("$BIN_ROOT/detect_os.sh") if [[ "$os" == "linux" ]]; then WARNINGS="-Wno-write-strings -Wno-comment" elif [[ "$os" == "mac" ]]; then -WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-switch" +WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-logical-op-parentheses -Wno-switch" fi FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" diff --git a/custom/4coder_base_types.cpp b/custom/4coder_base_types.cpp index 2209ff55..f3910953 100644 --- a/custom/4coder_base_types.cpp +++ b/custom/4coder_base_types.cpp @@ -6963,7 +6963,7 @@ global_const u8 base64_reverse[128] = { function u64 digit_count_from_integer(u64 x, u32 radix){ - u64 result = {}; + u64 result = 0; if (radix >= 2 && radix <= 16){ if (x == 0){ result = 1; diff --git a/custom/4coder_base_types.h b/custom/4coder_base_types.h index 3ca3f926..358485d9 100644 --- a/custom/4coder_base_types.h +++ b/custom/4coder_base_types.h @@ -31,14 +31,34 @@ # error architecture not supported yet # endif +#elif defined(__clang__) + +# define COMPILER_CLANG 1 + +# if defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error This compiler/platform combo is not supported yet +# endif + +# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define ARCH_X64 1 +# elif defined(i386) || defined(__i386) || defined(__i386__) +# define ARCH_X86 1 +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# elif defined(__arm__) +# define ARCH_ARM32 1 +# else +# error architecture not supported yet +# endif + #elif defined(__GNUC__) || defined(__GNUG__) # define COMPILER_GCC 1 # if defined(__gnu_linux__) # define OS_LINUX 1 -# elif defined(__APPLE__) && defined(__MACH__) -# define OS_MAC 1 # else # error This compiler/platform combo is not supported yet # endif @@ -92,6 +112,9 @@ #if !defined(COMPILER_GCC) #define COMPILER_GCC 0 #endif +#if !defined(COMPILER_CLANG) +#define COMPILER_CLANG 0 +#endif #if !defined(OS_WINDOWS) #define OS_WINDOWS 0 #endif @@ -836,7 +859,7 @@ enum{ struct String_Const_char{ char *str; - u64 size; + u64 size; }; struct String_Const_u8{ union{ @@ -924,25 +947,25 @@ struct Node_String_Const_u32{ struct List_String_Const_char{ Node_String_Const_char *first; Node_String_Const_char *last; - u64 total_size; + u64 total_size; i32 node_count; }; struct List_String_Const_u8{ Node_String_Const_u8 *first; Node_String_Const_u8 *last; - u64 total_size; + u64 total_size; i32 node_count; }; struct List_String_Const_u16{ Node_String_Const_u16 *first; Node_String_Const_u16 *last; - u64 total_size; + u64 total_size; i32 node_count; }; struct List_String_Const_u32{ Node_String_Const_u32 *first; Node_String_Const_u32 *last; - u64 total_size; + u64 total_size; i32 node_count; }; @@ -953,7 +976,7 @@ struct Node_String_Const_Any{ struct List_String_Const_Any{ Node_String_Const_Any *first; Node_String_Const_Any *last; - u64 total_size; + u64 total_size; i32 node_count; }; @@ -962,40 +985,40 @@ struct String_char{ String_Const_char string; struct{ char *str; - u64 size; + u64 size; }; }; - u64 cap; + u64 cap; }; struct String_u8{ union{ String_Const_u8 string; struct{ u8 *str; - u64 size; + u64 size; }; }; - u64 cap; + u64 cap; }; struct String_u16{ union{ String_Const_u16 string; struct{ u16 *str; - u64 size; + u64 size; }; }; - u64 cap; + u64 cap; }; struct String_u32{ union{ String_Const_u32 string; struct{ u32 *str; - u64 size; + u64 size; }; }; - u64 cap; + u64 cap; }; struct String_Any{ @@ -1003,8 +1026,8 @@ struct String_Any{ union{ struct{ void *str; - u64 size; - u64 cap; + u64 size; + u64 cap; }; String_char s_char; String_u8 s_u8; @@ -1091,7 +1114,7 @@ struct Base_Allocator{ struct Cursor{ u8 *base; - u64 pos; + u64 pos; u64 cap; }; struct Temp_Memory_Cursor{ @@ -1231,7 +1254,7 @@ struct Heap_Node{ struct{ Heap_Basic_Node order; Heap_Basic_Node alloc; - u64 size; + u64 size; }; u8 force_size__[64]; }; @@ -1242,8 +1265,8 @@ struct Heap{ Arena *arena; Heap_Basic_Node in_order; Heap_Basic_Node free_nodes; - u64 used_space; - u64 total_space; + u64 used_space; + u64 total_space; }; #endif diff --git a/custom/4coder_command_map.cpp b/custom/4coder_command_map.cpp index d2114d07..0d0ae016 100644 --- a/custom/4coder_command_map.cpp +++ b/custom/4coder_command_map.cpp @@ -425,21 +425,21 @@ function Command_Trigger_List map_get_triggers_recursive(Arena *arena, Mapping *mapping, Command_Map *map, Command_Binding binding){ Command_Trigger_List result = {}; if (mapping != 0){ - for (i32 safety_counter = 0; - map != 0 && safety_counter < 40; - safety_counter += 1){ - Command_Trigger_List list = map_get_triggers_non_recursive(mapping, map, binding); - - for (Command_Trigger *node = list.first, *next = 0; - node != 0; - node = next){ - next = node->next; - Command_Trigger *nnode = push_array_write(arena, Command_Trigger, 1, node); - sll_queue_push(result.first, result.last, nnode); + for (i32 safety_counter = 0; + map != 0 && safety_counter < 40; + safety_counter += 1){ + Command_Trigger_List list = map_get_triggers_non_recursive(mapping, map, binding); + + for (Command_Trigger *node = list.first, *next = 0; + node != 0; + node = next){ + next = node->next; + Command_Trigger *nnode = push_array_write(arena, Command_Trigger, 1, node); + sll_queue_push(result.first, result.last, nnode); + } + + map = mapping_get_map(mapping, map->parent); } - - map = mapping_get_map(mapping, map->parent); - } } return(result); } @@ -721,7 +721,7 @@ map_set_binding_l(Mapping *mapping, Command_Map *map, Custom_Command_Function *c va_start(args, code2); Command_Binding binding = {}; binding.custom = custom; - map_set_binding_lv(mapping, map, binding, code1, code2, args); + map_set_binding_lv(mapping, map, binding, code1, code2, args); va_end(args); } #endif @@ -755,7 +755,7 @@ map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseMove, 0, __VA_ARGS_ #define BindCore(F, K, ...) \ map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_Core, (K), __VA_ARGS__, 0) -#elif COMPILER_GCC +#elif COMPILER_GCC | COMPILER_CLANG #define Bind(F, K, ...) \ map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_KeyStroke, (K), ##__VA_ARGS__, 0) diff --git a/custom/4coder_file_moving.h b/custom/4coder_file_moving.h index b9beb7fe..3b8f0172 100644 --- a/custom/4coder_file_moving.h +++ b/custom/4coder_file_moving.h @@ -115,7 +115,7 @@ internal void fm__swap_ptr(char **A, char **B); fm__swap_ptr(&line.build_options, &line.build_options_prev); \ }while(0) -#elif COMPILER_GCC +#elif COMPILER_GCC | COMPILER_CLANG #define fm_add_to_line(line, str, ...) do{ \ snprintf(line.build_options, line.build_max, "%s " str, \ @@ -259,9 +259,9 @@ extern "C"{ #define OPEN_ALWAYS 4 #define TRUNCATE_EXISTING 5 -#define FILE_ATTRIBUTE_READONLY 0x00000001 -#define FILE_ATTRIBUTE_NORMAL 0x00000080 -#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 global u64 perf_frequency; @@ -370,9 +370,9 @@ fm_copy_file(char *file, char *newname){ internal void fm_copy_all(char *source, char *folder){ - fprintf(stdout, "copy %s to %s\n", source, folder); - fflush(stdout); - systemf("xcopy /s /e /y /q %s %s > nul", source, folder); + fprintf(stdout, "copy %s to %s\n", source, folder); + fflush(stdout); + systemf("xcopy /s /e /y /q %s %s > nul", source, folder); } internal void diff --git a/custom/bin/buildsuper_x64-mac.sh b/custom/bin/buildsuper_x64-mac.sh index e04e79cf..9f6bce10 100755 --- a/custom/bin/buildsuper_x64-mac.sh +++ b/custom/bin/buildsuper_x64-mac.sh @@ -15,16 +15,16 @@ if [ -z "$SOURCE" ]; then fi # NOTE(yuval): Removed -Wno-writable-strings as it is the same as -Wno-write-strings -opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -g" +opts="-Wno-write-strings -Wno-null-dereference -Wno-comment -Wno-switch -Wno-missing-declarations -Wno-logical-op-parentheses -g" arch=-m64 preproc_file=4coder_command_metadata.i meta_macros="-DMETA_PASS" -g++ -I"$CODE_HOME" $meta_macros $arch $opts $debug -std=gnu++0x "$SOURCE" -E -o $preproc_file -g++ -I"$CODE_HOME" $opts $debug -std=gnu++0x "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" +clang++ -I"$CODE_HOME" $meta_macros $arch $opts $debug -std=gnu++0x "$SOURCE" -E -o $preproc_file +clang++ -I"$CODE_HOME" $opts $debug -std=gnu++0x "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" "$CODE_HOME/metadata_generator" -R "$CODE_HOME" "$PWD/$preproc_file" -g++ -I"$CODE_HOME" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC +clang++ -I"$CODE_HOME" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC rm "$CODE_HOME/metadata_generator" rm $preproc_file diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index a6e993c2..46c04cd1 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -251,235 +251,235 @@ i32 source_name_len; i32 line_number; }; static Command_Metadata fcoder_metacmd_table[229] = { -{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "../code/custom/4coder_default_framework.cpp", 43, 409 }, -{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "../code/custom/4coder_auto_indent.cpp", 37, 375 }, -{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "../code/custom/4coder_auto_indent.cpp", 37, 385 }, -{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "../code/custom/4coder_auto_indent.cpp", 37, 366 }, -{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "../code/custom/4coder_base_commands.cpp", 39, 154 }, -{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "../code/custom/4coder_base_commands.cpp", 39, 96 }, -{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "../code/custom/4coder_base_commands.cpp", 39, 613 }, -{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "../code/custom/4coder_build_commands.cpp", 40, 165 }, -{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "../code/custom/4coder_build_commands.cpp", 40, 128 }, -{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "../code/custom/4coder_base_commands.cpp", 39, 197 }, -{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "../code/custom/4coder_default_framework.cpp", 43, 284 }, -{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "../code/custom/4coder_default_framework.cpp", 43, 290 }, -{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "../code/custom/4coder_build_commands.cpp", 40, 186 }, -{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "../code/custom/4coder_base_commands.cpp", 39, 578 }, -{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "../code/custom/4coder_default_framework.cpp", 43, 480 }, -{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "../code/custom/4coder_base_commands.cpp", 39, 233 }, -{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "../code/custom/4coder_base_commands.cpp", 39, 223 }, -{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "../code/custom/4coder_base_commands.cpp", 39, 243 }, -{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "../code/custom/4coder_base_commands.cpp", 39, 255 }, -{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "../code/custom/4coder_project_commands.cpp", 42, 842 }, -{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "../code/custom/4coder_build_commands.cpp", 40, 180 }, -{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "../code/custom/4coder_base_commands.cpp", 39, 621 }, -{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "../code/custom/4coder_docs.cpp", 30, 190 }, -{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "../code/custom/4coder_lists.cpp", 31, 668 }, -{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "../code/custom/4coder_combined_write_commands.cpp", 49, 125 }, -{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "../code/custom/4coder_combined_write_commands.cpp", 49, 149 }, -{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "../code/custom/4coder_clipboard.cpp", 35, 19 }, -{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "../code/custom/4coder_base_commands.cpp", 39, 124 }, -{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "../code/custom/4coder_docs.cpp", 30, 175 }, -{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "../code/custom/4coder_clipboard.cpp", 35, 28 }, -{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "../code/custom/4coder_base_commands.cpp", 39, 684 }, -{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "../code/custom/4coder_base_commands.cpp", 39, 1798 }, -{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "../code/custom/4coder_default_hooks.cpp", 39, 7 }, -{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "../code/custom/4coder_default_hooks.cpp", 39, 23 }, -{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "../code/custom/4coder_default_hooks.cpp", 39, 57 }, -{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "../code/custom/4coder_base_commands.cpp", 39, 162 }, -{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "../code/custom/4coder_base_commands.cpp", 39, 79 }, -{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "../code/custom/4coder_scope_commands.cpp", 40, 112 }, -{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "../code/custom/4coder_base_commands.cpp", 39, 1221 }, -{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "../code/custom/4coder_base_commands.cpp", 39, 1396 }, -{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "../code/custom/4coder_base_commands.cpp", 39, 134 }, -{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "../code/custom/4coder_base_commands.cpp", 39, 1382 }, -{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "../code/custom/4coder_cli_command.cpp", 37, 22 }, -{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "../code/custom/4coder_cli_command.cpp", 37, 7 }, -{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "../code/custom/4coder_base_commands.cpp", 39, 740 }, -{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "../code/custom/4coder_helper.cpp", 32, 2184 }, -{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "../code/custom/4coder_helper.cpp", 32, 2192 }, -{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "../code/custom/4coder_jump_sticky.cpp", 37, 523 }, -{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "../code/custom/4coder_jump_sticky.cpp", 37, 540 }, -{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "../code/custom/4coder_jump_sticky.cpp", 37, 346 }, -{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "../code/custom/4coder_jump_sticky.cpp", 37, 373 }, -{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "../code/custom/4coder_base_commands.cpp", 39, 748 }, -{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "../code/custom/4coder_jump_sticky.cpp", 37, 462 }, -{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "../code/custom/4coder_jump_sticky.cpp", 37, 492 }, -{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "../code/custom/4coder_jump_sticky.cpp", 37, 479 }, -{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "../code/custom/4coder_jump_sticky.cpp", 37, 509 }, -{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "../code/custom/4coder_base_commands.cpp", 39, 651 }, -{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "../code/custom/4coder_base_commands.cpp", 39, 637 }, -{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "../code/custom/4coder_tutorial.cpp", 34, 869 }, -{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "../code/custom/4coder_combined_write_commands.cpp", 49, 70 }, -{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "../code/custom/4coder_jump_sticky.cpp", 37, 562 }, -{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "../code/custom/4coder_jump_sticky.cpp", 37, 579 }, -{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "../code/custom/4coder_base_commands.cpp", 39, 673 }, -{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "../code/custom/4coder_lists.cpp", 31, 515 }, -{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "../code/custom/4coder_lists.cpp", 31, 597 }, -{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "../code/custom/4coder_lists.cpp", 31, 634 }, -{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "../code/custom/4coder_lists.cpp", 31, 563 }, -{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "../code/custom/4coder_lists.cpp", 31, 505 }, -{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "../code/custom/4coder_code_index_listers.cpp", 44, 12 }, -{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "../code/custom/4coder_keyboard_macro.cpp", 40, 57 }, -{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "../code/custom/4coder_keyboard_macro.cpp", 40, 80 }, -{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "../code/custom/4coder_keyboard_macro.cpp", 40, 44 }, -{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "../code/custom/4coder_base_commands.cpp", 39, 1542 }, -{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "../code/custom/4coder_tutorial.cpp", 34, 9 }, -{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "../code/custom/4coder_base_commands.cpp", 39, 211 }, -{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "../code/custom/4coder_function_list.cpp", 39, 295 }, -{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "../code/custom/4coder_function_list.cpp", 39, 301 }, -{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "../code/custom/4coder_function_list.cpp", 39, 267 }, -{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "../code/custom/4coder_function_list.cpp", 39, 277 }, -{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "../code/custom/4coder_search.cpp", 32, 162 }, -{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "../code/custom/4coder_search.cpp", 32, 174 }, -{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "../code/custom/4coder_search.cpp", 32, 186 }, -{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "../code/custom/4coder_search.cpp", 32, 192 }, -{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "../code/custom/4coder_search.cpp", 32, 198 }, -{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "../code/custom/4coder_search.cpp", 32, 204 }, -{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "../code/custom/4coder_search.cpp", 32, 210 }, -{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "../code/custom/4coder_search.cpp", 32, 218 }, -{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "../code/custom/4coder_search.cpp", 32, 168 }, -{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "../code/custom/4coder_search.cpp", 32, 180 }, -{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "../code/custom/4coder_project_commands.cpp", 42, 862 }, -{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "../code/custom/4coder_default_framework.cpp", 43, 457 }, -{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "../code/custom/4coder_default_framework.cpp", 43, 469 }, -{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "../code/custom/4coder_base_commands.cpp", 39, 1336 }, -{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "../code/custom/4coder_miblo_numbers.cpp", 39, 44 }, -{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 237 }, -{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 249 }, -{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "../code/custom/4coder_miblo_numbers.cpp", 39, 29 }, -{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 231 }, -{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "../code/custom/4coder_miblo_numbers.cpp", 39, 243 }, -{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "../code/custom/4coder_base_commands.cpp", 39, 695 }, -{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "../code/custom/4coder_base_commands.cpp", 39, 265 }, -{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "../code/custom/4coder_base_commands.cpp", 39, 338 }, -{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "../code/custom/4coder_base_commands.cpp", 39, 350 }, -{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "../code/custom/4coder_base_commands.cpp", 39, 356 }, -{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "../code/custom/4coder_base_commands.cpp", 39, 409 }, -{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "../code/custom/4coder_base_commands.cpp", 39, 433 }, -{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "../code/custom/4coder_base_commands.cpp", 39, 421 }, -{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "../code/custom/4coder_base_commands.cpp", 39, 439 }, -{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "../code/custom/4coder_base_commands.cpp", 39, 516 }, -{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "../code/custom/4coder_base_commands.cpp", 39, 530 }, -{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "../code/custom/4coder_base_commands.cpp", 39, 488 }, -{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "../code/custom/4coder_base_commands.cpp", 39, 473 }, -{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "../code/custom/4coder_base_commands.cpp", 39, 502 }, -{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "../code/custom/4coder_base_commands.cpp", 39, 1376 }, -{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "../code/custom/4coder_base_commands.cpp", 39, 1370 }, -{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "../code/custom/4coder_base_commands.cpp", 39, 447 }, -{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "../code/custom/4coder_base_commands.cpp", 39, 509 }, -{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "../code/custom/4coder_base_commands.cpp", 39, 523 }, -{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "../code/custom/4coder_base_commands.cpp", 39, 481 }, -{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "../code/custom/4coder_base_commands.cpp", 39, 465 }, -{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "../code/custom/4coder_base_commands.cpp", 39, 495 }, -{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "../code/custom/4coder_base_commands.cpp", 39, 332 }, -{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "../code/custom/4coder_base_commands.cpp", 39, 344 }, -{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "../code/custom/4coder_base_commands.cpp", 39, 403 }, -{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "../code/custom/4coder_base_commands.cpp", 39, 427 }, -{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "../code/custom/4coder_base_commands.cpp", 39, 415 }, -{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "../code/custom/4coder_project_commands.cpp", 42, 848 }, -{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "../code/custom/4coder_project_commands.cpp", 42, 854 }, -{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "../code/custom/4coder_base_commands.cpp", 39, 1461 }, -{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "../code/custom/4coder_base_commands.cpp", 39, 1792 }, -{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "../code/custom/4coder_combined_write_commands.cpp", 49, 46 }, -{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "../code/custom/4coder_combined_write_commands.cpp", 49, 62 }, -{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "../code/custom/4coder_combined_write_commands.cpp", 49, 54 }, -{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "../code/custom/4coder_base_commands.cpp", 39, 1493 }, -{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "../code/custom/4coder_default_framework.cpp", 43, 310 }, -{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "../code/custom/4coder_default_framework.cpp", 43, 300 }, -{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "../code/custom/4coder_base_commands.cpp", 39, 374 }, -{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "../code/custom/4coder_base_commands.cpp", 39, 366 }, -{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "../code/custom/4coder_clipboard.cpp", 35, 39 }, -{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "../code/custom/4coder_clipboard.cpp", 35, 110 }, -{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "../code/custom/4coder_clipboard.cpp", 35, 71 }, -{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "../code/custom/4coder_clipboard.cpp", 35, 117 }, -{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "../code/custom/4coder_scope_commands.cpp", 40, 106 }, -{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "../code/custom/4coder_profile.cpp", 33, 226 }, -{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "../code/custom/4coder_profile.cpp", 33, 219 }, -{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "../code/custom/4coder_profile.cpp", 33, 212 }, -{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "../code/custom/4coder_profile_inspect.cpp", 41, 886 }, -{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "../code/custom/4coder_project_commands.cpp", 42, 1289 }, -{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "../code/custom/4coder_project_commands.cpp", 42, 870 }, -{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "../code/custom/4coder_project_commands.cpp", 42, 896 }, -{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "../code/custom/4coder_base_commands.cpp", 39, 1149 }, -{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "../code/custom/4coder_base_commands.cpp", 39, 1170 }, -{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "../code/custom/4coder_base_commands.cpp", 39, 1186 }, -{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "../code/custom/4coder_base_commands.cpp", 39, 1631 }, -{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "../code/custom/4coder_base_commands.cpp", 39, 1716 }, -{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "../code/custom/4coder_base_commands.cpp", 39, 1298 }, -{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "../code/custom/4coder_base_commands.cpp", 39, 1560 }, -{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "../code/custom/4coder_base_commands.cpp", 39, 1059 }, -{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "../code/custom/4coder_base_commands.cpp", 39, 1050 }, -{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "../code/custom/4coder_base_commands.cpp", 39, 1041 }, -{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "../code/custom/4coder_base_commands.cpp", 39, 982 }, -{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "../code/custom/4coder_base_commands.cpp", 39, 994 }, -{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "../code/custom/4coder_base_commands.cpp", 39, 1550 }, -{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "../code/custom/4coder_default_framework.cpp", 43, 382 }, -{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "../code/custom/4coder_base_commands.cpp", 39, 1265 }, -{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "../code/custom/4coder_base_commands.cpp", 39, 976 }, -{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "../code/custom/4coder_base_commands.cpp", 39, 988 }, -{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "../code/custom/4coder_helper.cpp", 32, 2172 }, -{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "../code/custom/4coder_helper.cpp", 32, 2160 }, -{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "../code/custom/4coder_helper.cpp", 32, 2178 }, -{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "../code/custom/4coder_helper.cpp", 32, 2166 }, -{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "../code/custom/4coder_base_commands.cpp", 39, 539 }, -{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "../code/custom/4coder_scope_commands.cpp", 40, 57 }, -{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "../code/custom/4coder_scope_commands.cpp", 40, 66 }, -{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "../code/custom/4coder_scope_commands.cpp", 40, 82 }, -{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "../code/custom/4coder_scope_commands.cpp", 40, 99 }, -{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "../code/custom/4coder_scope_commands.cpp", 40, 27 }, -{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "../code/custom/4coder_scope_commands.cpp", 40, 39 }, -{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "../code/custom/4coder_eol.cpp", 29, 125 }, -{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "../code/custom/4coder_eol.cpp", 29, 112 }, -{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "../code/custom/4coder_eol.cpp", 29, 86 }, -{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "../code/custom/4coder_eol.cpp", 29, 99 }, -{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "../code/custom/4coder_base_commands.cpp", 39, 115 }, -{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "../code/custom/4coder_default_framework.cpp", 43, 427 }, -{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "../code/custom/4coder_default_framework.cpp", 43, 421 }, -{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1237 }, -{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1249 }, -{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "../code/custom/4coder_project_commands.cpp", 42, 1243 }, -{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "../code/custom/4coder_project_commands.cpp", 42, 1230 }, -{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "../code/custom/4coder_base_commands.cpp", 39, 644 }, -{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "../code/custom/4coder_base_commands.cpp", 39, 630 }, -{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "../code/custom/4coder_log_parser.cpp", 36, 994 }, -{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "../code/custom/4coder_base_commands.cpp", 39, 179 }, -{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "../code/custom/4coder_base_commands.cpp", 39, 187 }, -{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "../code/custom/4coder_combined_write_commands.cpp", 49, 237 }, -{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "../code/custom/4coder_default_framework.cpp", 43, 403 }, -{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "../code/custom/4coder_base_commands.cpp", 39, 1518 }, -{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "../code/custom/4coder_lists.cpp", 31, 692 }, -{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "../code/custom/4coder_base_commands.cpp", 39, 565 }, -{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "../code/custom/4coder_base_commands.cpp", 39, 552 }, -{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "../code/custom/4coder_base_commands.cpp", 39, 658 }, -{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "../code/custom/4coder_base_commands.cpp", 39, 667 }, -{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "../code/custom/4coder_default_framework.cpp", 43, 451 }, -{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "../code/custom/4coder_default_framework.cpp", 43, 439 }, -{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "../code/custom/4coder_default_framework.cpp", 43, 433 }, -{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "../code/custom/4coder_base_commands.cpp", 39, 721 }, -{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "../code/custom/4coder_base_commands.cpp", 39, 727 }, -{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "../code/custom/4coder_default_framework.cpp", 43, 415 }, -{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "../code/custom/4coder_default_framework.cpp", 43, 445 }, -{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "../code/custom/4coder_base_commands.cpp", 39, 712 }, -{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "../code/custom/4coder_code_index.cpp", 36, 1160 }, -{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "../code/custom/4coder_tutorial.cpp", 34, 20 }, -{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "../code/custom/4coder_tutorial.cpp", 34, 34 }, -{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "../code/custom/4coder_combined_write_commands.cpp", 49, 137 }, -{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "../code/custom/4coder_base_commands.cpp", 39, 1618 }, -{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "../code/custom/4coder_base_commands.cpp", 39, 1645 }, -{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "../code/custom/4coder_base_commands.cpp", 39, 1506 }, -{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "../code/custom/4coder_jump_lister.cpp", 37, 59 }, -{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "../code/custom/4coder_search.cpp", 32, 392 }, -{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "../code/custom/4coder_search.cpp", 32, 639 }, -{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "../code/custom/4coder_combined_write_commands.cpp", 49, 94 }, -{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 82 }, -{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 88 }, -{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "../code/custom/4coder_base_commands.cpp", 39, 67 }, -{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "../code/custom/4coder_auto_indent.cpp", 37, 395 }, -{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "../code/custom/4coder_base_commands.cpp", 39, 59 }, -{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "../code/custom/4coder_combined_write_commands.cpp", 49, 76 }, -{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "../code/custom/4coder_base_commands.cpp", 39, 73 }, -{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "../code/custom/4coder_combined_write_commands.cpp", 49, 100 }, +{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 409 }, +{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 375 }, +{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 385 }, +{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 366 }, +{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 154 }, +{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 96 }, +{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 613 }, +{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 165 }, +{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 128 }, +{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 197 }, +{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 284 }, +{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 290 }, +{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 186 }, +{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 578 }, +{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 480 }, +{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 233 }, +{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 223 }, +{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 243 }, +{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 255 }, +{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 842 }, +{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 180 }, +{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 621 }, +{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_docs.cpp", 49, 190 }, +{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 668 }, +{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 125 }, +{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 149 }, +{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 19 }, +{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 124 }, +{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_docs.cpp", 49, 175 }, +{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 28 }, +{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 684 }, +{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1798 }, +{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 7 }, +{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 23 }, +{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 57 }, +{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 162 }, +{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 79 }, +{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 112 }, +{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1221 }, +{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1396 }, +{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 134 }, +{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1382 }, +{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "/Users/yuvaldolev/4ed/code/custom/4coder_cli_command.cpp", 56, 22 }, +{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "/Users/yuvaldolev/4ed/code/custom/4coder_cli_command.cpp", 56, 7 }, +{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 740 }, +{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2184 }, +{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2192 }, +{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 523 }, +{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 540 }, +{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 346 }, +{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 373 }, +{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 748 }, +{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 462 }, +{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 492 }, +{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 479 }, +{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 509 }, +{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 651 }, +{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 637 }, +{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 869 }, +{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 70 }, +{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 562 }, +{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 579 }, +{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 673 }, +{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 515 }, +{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 597 }, +{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 634 }, +{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 563 }, +{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 505 }, +{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_code_index_listers.cpp", 63, 12 }, +{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 57 }, +{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 80 }, +{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 44 }, +{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1542 }, +{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 9 }, +{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 211 }, +{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 295 }, +{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 301 }, +{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 267 }, +{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 277 }, +{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 162 }, +{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 174 }, +{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 186 }, +{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 192 }, +{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 198 }, +{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 204 }, +{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 210 }, +{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 218 }, +{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 168 }, +{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 180 }, +{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 862 }, +{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 457 }, +{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 469 }, +{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1336 }, +{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 44 }, +{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 237 }, +{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 249 }, +{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 29 }, +{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 231 }, +{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 243 }, +{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 695 }, +{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 265 }, +{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 338 }, +{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 350 }, +{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 356 }, +{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 409 }, +{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 433 }, +{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 421 }, +{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 439 }, +{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 516 }, +{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 530 }, +{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 488 }, +{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 473 }, +{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 502 }, +{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1376 }, +{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1370 }, +{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 447 }, +{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 509 }, +{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 523 }, +{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 481 }, +{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 465 }, +{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 495 }, +{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 332 }, +{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 344 }, +{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 403 }, +{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 427 }, +{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 415 }, +{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 848 }, +{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 854 }, +{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1461 }, +{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1792 }, +{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 46 }, +{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 62 }, +{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 54 }, +{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1493 }, +{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 310 }, +{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 300 }, +{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 374 }, +{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 366 }, +{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 39 }, +{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 110 }, +{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 71 }, +{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 117 }, +{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 106 }, +{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 226 }, +{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 219 }, +{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 212 }, +{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_profile_inspect.cpp", 60, 886 }, +{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1289 }, +{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 870 }, +{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 896 }, +{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1149 }, +{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1170 }, +{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1186 }, +{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1631 }, +{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1716 }, +{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1298 }, +{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1560 }, +{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1059 }, +{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1050 }, +{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1041 }, +{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 982 }, +{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 994 }, +{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1550 }, +{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 382 }, +{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1265 }, +{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 976 }, +{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 988 }, +{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2172 }, +{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2160 }, +{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2178 }, +{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2166 }, +{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 539 }, +{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 57 }, +{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 66 }, +{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 82 }, +{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 99 }, +{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 27 }, +{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 39 }, +{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 125 }, +{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 112 }, +{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 86 }, +{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 99 }, +{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 115 }, +{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 427 }, +{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 421 }, +{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1237 }, +{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1249 }, +{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1243 }, +{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1230 }, +{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 644 }, +{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 630 }, +{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_log_parser.cpp", 55, 994 }, +{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 179 }, +{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 187 }, +{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 237 }, +{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 403 }, +{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1518 }, +{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 692 }, +{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 565 }, +{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 552 }, +{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 658 }, +{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 667 }, +{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 451 }, +{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 439 }, +{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 433 }, +{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 721 }, +{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 727 }, +{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 415 }, +{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 445 }, +{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 712 }, +{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_code_index.cpp", 55, 1160 }, +{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 20 }, +{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 34 }, +{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 137 }, +{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1618 }, +{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1645 }, +{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1506 }, +{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_lister.cpp", 56, 59 }, +{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 392 }, +{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 639 }, +{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 94 }, +{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 82 }, +{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 88 }, +{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 67 }, +{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 395 }, +{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 59 }, +{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 76 }, +{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 73 }, +{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 100 }, }; static i32 fcoder_metacmd_ID_allow_mouse = 0; static i32 fcoder_metacmd_ID_auto_indent_line_at_cursor = 1; diff --git a/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator b/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator index 28c1007f45ecd0a4f588766b5e518e8d7fe6dc50..5c9e294020b8a7516616fb4a8862795a763a2d5f 100644 GIT binary patch literal 136740 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 Yd#&*^$M^S#2(V=a_-h7h($ diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 8182c122..34991d33 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,4 +1,4 @@ -// #include +#include int main(int arg_count, char **args){ From 18dd91197fca9c4666744852f1fcb204f1a3d7a3 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 24 Dec 2019 02:53:25 +0200 Subject: [PATCH 07/67] The new (very basic) macOS platform layer compiles! --- platform_mac/mac_4ed.mm | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 34991d33..0c7a2c5e 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,5 +1,29 @@ #include +@interface App_Delegate : NSObject +@end + +@implementation App_Delegate +- (void)applicationDidFinishLaunching:(id)sender{ +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender{ + return YES; +} + +- (void)applicationWillTerminate:(NSNotification *)notification{ +} + +- (NSSize)windowWillResize:(NSWindow*)window toSize:(NSSize)frame_size{ + // frame_size.height = ((f32)frame_size.width / global_aspect_ratio); + return frame_size; +} + +- (void)windowWillClose:(id)sender { + // global_running = false; +} +@end + int main(int arg_count, char **args){ @autoreleasepool{ From 54f5e72aa50e94ffa460324755b538cd422caad0 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 24 Dec 2019 16:57:05 +0200 Subject: [PATCH 08/67] Added a c++ platform layer file. --- platform_mac/mac_4ed.cpp | 1 + platform_mac/mac_4ed.mm | 51 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 platform_mac/mac_4ed.cpp diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp new file mode 100644 index 00000000..a5486f8e --- /dev/null +++ b/platform_mac/mac_4ed.cpp @@ -0,0 +1 @@ +/* Mac C++ layer for 4coder */ \ No newline at end of file diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 0c7a2c5e..52390586 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,5 +1,36 @@ +/* Mac Objective C layer for 4coder */ + #include +#define FPS 60 +#define frame_useconds (1000000 / FPS) + +#include "4coder_base_types.h" +#include "4coder_version.h" +#include "4coder_events.h" + +#include "4coder_system_types.h" +#define STATIC_LINK_API +#include "generated/system_api.h" + +#include "generated/system_api.cpp" + +#include "4coder_base_types.cpp" + +//////////////////////////////// + +#define SLASH '\\' +#define DLL "dll" + +#include "4coder_hash_functions.cpp" +#include "4coder_system_allocator.cpp" +#include "4coder_codepoint_map.cpp" + +#include "4ed_mem.cpp" +#include "4ed_font_set.cpp" + +//////////////////////////////// + @interface App_Delegate : NSObject @end @@ -19,7 +50,7 @@ return frame_size; } -- (void)windowWillClose:(id)sender { +- (void)windowWillClose:(id)sender{ // global_running = false; } @end @@ -27,7 +58,7 @@ int main(int arg_count, char **args){ @autoreleasepool{ - // NOTE(yuval): NSApplication & Delegate Creation + // NOTE(yuval): Create NSApplication & Delegate NSApplication* app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; @@ -36,7 +67,7 @@ main(int arg_count, char **args){ [NSApp finishLaunching]; -#if 0 + // NOTE(yuval): Context Setup Thread_Context _tctx = {}; thread_ctx_init(&_tctx, ThreadKind_Main, get_base_allocator_system(), @@ -45,11 +76,23 @@ main(int arg_count, char **args){ block_zero_struct(&global_mac_vars); global_mac_vars.tctx = &_tctx; + API_VTable_system system_vtable = {}; + system_api_fill_vtable(&system_vtable); + + API_VTable_graphics graphics_vtable = {}; + graphics_api_fill_vtable(&graphics_vtable); + + API_VTable_font font_vtable = {}; + font_api_fill_vtable(&font_vtable); + + // NOTE(yuval): Memory + global_mac_vars.frame_arena = reserve_arena(global_mac_vars.tctx); + global_target.arean = make_arena_system(KB(256)); + // NOTE(yuval): Application Core Update Application_Step_Result result = {}; if (app.step != 0){ result = app.step(mac_vars.tctx, &target, base_ptr, &input); } -#endif } } \ No newline at end of file From e3e5f857ea5c964fc2fabdce076b60d3a3c8ff07 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 24 Dec 2019 22:31:24 +0200 Subject: [PATCH 09/67] Added empty implementations of all the system api functions to mac_4ed_functions.cpp --- bin/4ed_build.cpp | 2 +- custom/generated/system_api.h | 94 ++--- platform_mac/mac_4ed.cpp | 94 ++++- platform_mac/mac_4ed.mm | 58 +-- platform_mac/mac_4ed_functions.cpp | 348 ++++++++++++++++++ platform_mac/mac_4ed_old.cpp | 2 +- platform_mac/mac_4ed_old.m | 16 +- platform_mac/mac_objective_c_to_cpp_links.h | 13 + ...s.h => osx_objective_c_to_cpp_links_old.h} | 0 9 files changed, 518 insertions(+), 109 deletions(-) create mode 100644 platform_mac/mac_4ed_functions.cpp create mode 100644 platform_mac/mac_objective_c_to_cpp_links.h rename platform_mac/{osx_objective_c_to_cpp_links.h => osx_objective_c_to_cpp_links_old.h} (100%) diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 146169fb..f15d0040 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -94,7 +94,7 @@ char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", 0 }; +char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", "platform_mac/mac_4ed.cpp", 0 }; char **platform_layers[Platform_COUNT] = { windows_platform_layer, diff --git a/custom/generated/system_api.h b/custom/generated/system_api.h index ce9536da..f67a518f 100644 --- a/custom/generated/system_api.h +++ b/custom/generated/system_api.h @@ -93,53 +93,53 @@ typedef b32 system_set_fullscreen_type(b32 full_screen); typedef b32 system_is_fullscreen_type(void); typedef Input_Modifier_Set system_get_keyboard_modifiers_type(Arena* arena); struct API_VTable_system{ -system_get_path_type *get_path; -system_get_canonical_type *get_canonical; -system_get_file_list_type *get_file_list; -system_quick_file_attributes_type *quick_file_attributes; -system_load_handle_type *load_handle; -system_load_attributes_type *load_attributes; -system_load_file_type *load_file; -system_load_close_type *load_close; -system_save_file_type *save_file; -system_load_library_type *load_library; -system_release_library_type *release_library; -system_get_proc_type *get_proc; -system_now_time_type *now_time; -system_wake_up_timer_create_type *wake_up_timer_create; -system_wake_up_timer_release_type *wake_up_timer_release; -system_wake_up_timer_set_type *wake_up_timer_set; -system_signal_step_type *signal_step; -system_sleep_type *sleep; -system_post_clipboard_type *post_clipboard; -system_cli_call_type *cli_call; -system_cli_begin_update_type *cli_begin_update; -system_cli_update_step_type *cli_update_step; -system_cli_end_update_type *cli_end_update; -system_open_color_picker_type *open_color_picker; -system_get_screen_scale_factor_type *get_screen_scale_factor; -system_thread_launch_type *thread_launch; -system_thread_join_type *thread_join; -system_thread_free_type *thread_free; -system_thread_get_id_type *thread_get_id; -system_acquire_global_frame_mutex_type *acquire_global_frame_mutex; -system_release_global_frame_mutex_type *release_global_frame_mutex; -system_mutex_make_type *mutex_make; -system_mutex_acquire_type *mutex_acquire; -system_mutex_release_type *mutex_release; -system_mutex_free_type *mutex_free; -system_condition_variable_make_type *condition_variable_make; -system_condition_variable_wait_type *condition_variable_wait; -system_condition_variable_signal_type *condition_variable_signal; -system_condition_variable_free_type *condition_variable_free; -system_memory_allocate_type *memory_allocate; -system_memory_set_protection_type *memory_set_protection; -system_memory_free_type *memory_free; -system_memory_annotation_type *memory_annotation; -system_show_mouse_cursor_type *show_mouse_cursor; -system_set_fullscreen_type *set_fullscreen; -system_is_fullscreen_type *is_fullscreen; -system_get_keyboard_modifiers_type *get_keyboard_modifiers; + system_get_path_type *get_path; + system_get_canonical_type *get_canonical; + system_get_file_list_type *get_file_list; + system_quick_file_attributes_type *quick_file_attributes; + system_load_handle_type *load_handle; + system_load_attributes_type *load_attributes; + system_load_file_type *load_file; + system_load_close_type *load_close; + system_save_file_type *save_file; + system_load_library_type *load_library; + system_release_library_type *release_library; + system_get_proc_type *get_proc; + system_now_time_type *now_time; + system_wake_up_timer_create_type *wake_up_timer_create; + system_wake_up_timer_release_type *wake_up_timer_release; + system_wake_up_timer_set_type *wake_up_timer_set; + system_signal_step_type *signal_step; + system_sleep_type *sleep; + system_post_clipboard_type *post_clipboard; + system_cli_call_type *cli_call; + system_cli_begin_update_type *cli_begin_update; + system_cli_update_step_type *cli_update_step; + system_cli_end_update_type *cli_end_update; + system_open_color_picker_type *open_color_picker; + system_get_screen_scale_factor_type *get_screen_scale_factor; + system_thread_launch_type *thread_launch; + system_thread_join_type *thread_join; + system_thread_free_type *thread_free; + system_thread_get_id_type *thread_get_id; + system_acquire_global_frame_mutex_type *acquire_global_frame_mutex; + system_release_global_frame_mutex_type *release_global_frame_mutex; + system_mutex_make_type *mutex_make; + system_mutex_acquire_type *mutex_acquire; + system_mutex_release_type *mutex_release; + system_mutex_free_type *mutex_free; + system_condition_variable_make_type *condition_variable_make; + system_condition_variable_wait_type *condition_variable_wait; + system_condition_variable_signal_type *condition_variable_signal; + system_condition_variable_free_type *condition_variable_free; + system_memory_allocate_type *memory_allocate; + system_memory_set_protection_type *memory_set_protection; + system_memory_free_type *memory_free; + system_memory_annotation_type *memory_annotation; + system_show_mouse_cursor_type *show_mouse_cursor; + system_set_fullscreen_type *set_fullscreen; + system_is_fullscreen_type *is_fullscreen; + system_get_keyboard_modifiers_type *get_keyboard_modifiers; }; #if defined(STATIC_LINK_API) internal String_Const_u8 system_get_path(Arena* arena, System_Path_Code path_code); diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index a5486f8e..7272be87 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -1 +1,93 @@ -/* Mac C++ layer for 4coder */ \ No newline at end of file +/* Mac C++ layer for 4coder */ + +#include "4coder_base_types.h" +#include "4coder_version.h" +#include "4coder_events.h" + +#include "4coder_table.h" +#include "4coder_types.h" +#include "4coder_default_colors.h" + +#include "4coder_system_types.h" +#define STATIC_LINK_API +#include "generated/system_api.h" + +#include "4ed_font_interface.h" +#define STATIC_LINK_API +#include "generated/graphics_api.h" +#define STATIC_LINK_API +#include "generated/font_api.h" + +#include "4ed_font_set.h" +#include "4ed_render_target.h" +#include "4ed_search_list.h" +#include "4ed.h" + +#include "generated/system_api.cpp" +#include "generated/graphics_api.cpp" +#include "generated/font_api.cpp" + +#include "4coder_base_types.cpp" +#include "4coder_stringf.cpp" +#include "4coder_events.cpp" +#include "4coder_hash_functions.cpp" +#include "4coder_table.cpp" +#include "4coder_log.cpp" + +#include "4ed_search_list.cpp" + +//////////////////////////////// + +#define SLASH '\\' +#define DLL "dll" + +#include "4coder_hash_functions.cpp" +#include "4coder_system_allocator.cpp" +#include "4coder_codepoint_map.cpp" + +#include "4ed_mem.cpp" +#include "4ed_font_set.cpp" + +//////////////////////////////// + +struct Mac_Vars { + Thread_Context *tctx; + + Arena* frame_arena; +}; + +//////////////////////////////// + +Mac_Vars global_mac_vars; +global Render_Target global_target; + +//////////////////////////////// + +#include "mac_4ed_functions.cpp" + +//////////////////////////////// + +external void +mac_init() { + // NOTE(yuval): Context Setup + Thread_Context _tctx = {}; + thread_ctx_init(&_tctx, ThreadKind_Main, + get_base_allocator_system(), + get_base_allocator_system()); + + block_zero_struct(&global_mac_vars); + global_mac_vars.tctx = &_tctx; + + API_VTable_system system_vtable = {}; + system_api_fill_vtable(&system_vtable); + + API_VTable_graphics graphics_vtable = {}; + graphics_api_fill_vtable(&graphics_vtable); + + API_VTable_font font_vtable = {}; + font_api_fill_vtable(&font_vtable); + + // NOTE(yuval): Memory + global_mac_vars.frame_arena = reserve_arena(global_mac_vars.tctx); + global_target.arena = make_arena_system(KB(256)); +} \ No newline at end of file diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 52390586..538e2f4a 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,35 +1,13 @@ /* Mac Objective C layer for 4coder */ -#include - -#define FPS 60 -#define frame_useconds (1000000 / FPS) - #include "4coder_base_types.h" -#include "4coder_version.h" -#include "4coder_events.h" +#include "mac_objective_c_to_cpp_links.h" -#include "4coder_system_types.h" -#define STATIC_LINK_API -#include "generated/system_api.h" - -#include "generated/system_api.cpp" - -#include "4coder_base_types.cpp" - -//////////////////////////////// - -#define SLASH '\\' -#define DLL "dll" - -#include "4coder_hash_functions.cpp" -#include "4coder_system_allocator.cpp" -#include "4coder_codepoint_map.cpp" - -#include "4ed_mem.cpp" -#include "4ed_font_set.cpp" - -//////////////////////////////// +#undef function +#undef internal +#undef global +#undef external +#include @interface App_Delegate : NSObject @end @@ -67,32 +45,14 @@ main(int arg_count, char **args){ [NSApp finishLaunching]; - // NOTE(yuval): Context Setup - Thread_Context _tctx = {}; - thread_ctx_init(&_tctx, ThreadKind_Main, - get_base_allocator_system(), - get_base_allocator_system()); - - block_zero_struct(&global_mac_vars); - global_mac_vars.tctx = &_tctx; - - API_VTable_system system_vtable = {}; - system_api_fill_vtable(&system_vtable); - - API_VTable_graphics graphics_vtable = {}; - graphics_api_fill_vtable(&graphics_vtable); - - API_VTable_font font_vtable = {}; - font_api_fill_vtable(&font_vtable); - - // NOTE(yuval): Memory - global_mac_vars.frame_arena = reserve_arena(global_mac_vars.tctx); - global_target.arean = make_arena_system(KB(256)); + mac_init(); +#if 0 // NOTE(yuval): Application Core Update Application_Step_Result result = {}; if (app.step != 0){ result = app.step(mac_vars.tctx, &target, base_ptr, &input); } +#endif } } \ No newline at end of file diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp new file mode 100644 index 00000000..7288250c --- /dev/null +++ b/platform_mac/mac_4ed_functions.cpp @@ -0,0 +1,348 @@ +/* General macOS Functions */ + +function +system_get_path_sig(){ + String_Const_u8 result = {}; + + NotImplemented; + + return result; +} + +function +system_get_canonical_sig(){ + String_Const_u8 result = {}; + + NotImplemented; + + return result; +} + +function +system_get_file_list_sig(){ + File_List result = {}; + + NotImplemented; + + return result; +} + +function +system_quick_file_attributes_sig(){ + File_Attributes result = {}; + + NotImplemented; + + return result; +} + +function +system_load_handle_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_load_attributes_sig(){ + File_Attributes result = {}; + + NotImplemented; + + return result; +} + +function +system_load_file_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_load_close_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_save_file_sig(){ + File_Attributes result = {}; + + NotImplemented; + + return result; +} + +function +system_load_library_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_release_library_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_get_proc_sig(){ + Void_Func* result = 0; + + NotImplemented; + + return result; +} + +function +system_now_time_sig(){ + u64 result = 0; + + NotImplemented; + + return result; +} + +function +system_wake_up_timer_create_sig(){ + Plat_Handle result = {}; + + NotImplemented; + + return result; +} + +function +system_wake_up_timer_release_sig(){ + NotImplemented; +} + +function +system_wake_up_timer_set_sig(){ + NotImplemented; +} + +function +system_signal_step_sig(){ + NotImplemented; +} + +function +system_sleep_sig(){ + NotImplemented; +} + +function +system_post_clipboard_sig(){ + NotImplemented; +} + +function +system_cli_call_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_cli_begin_update_sig(){ + NotImplemented; +} + +function +system_cli_update_step_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_cli_end_update_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_open_color_picker_sig(){ + NotImplemented; +} + +function +system_get_screen_scale_factor_sig(){ + f32 result = 0.0f; + + NotImplemented; + + return result; +} + +function +system_thread_launch_sig(){ + System_Thread result = {}; + + NotImplemented; + + return result; +} + +function +system_thread_join_sig(){ + NotImplemented; +} + +function +system_thread_free_sig(){ + NotImplemented; +} + +function +system_thread_get_id_sig(){ + i32 result = 0; + + NotImplemented; + + return result; +} + +function +system_acquire_global_frame_mutex_sig(){ + NotImplemented; +} + +function +system_release_global_frame_mutex_sig(){ + NotImplemented; +} + +function +system_mutex_make_sig(){ + System_Mutex result = {}; + + NotImplemented; + + return result; +} + +function +system_mutex_acquire_sig(){ + NotImplemented; +} + +function +system_mutex_release_sig(){ + NotImplemented; +} + +function +system_mutex_free_sig(){ + NotImplemented; +} + +function +system_condition_variable_make_sig(){ + System_Condition_Variable result = {}; + + NotImplemented; + + return result; +} + +function +system_condition_variable_wait_sig(){ + NotImplemented; +} + +function +system_condition_variable_signal_sig(){ + NotImplemented; +} + +function +system_condition_variable_free_sig(){ + NotImplemented; +} + +function +system_memory_allocate_sig(){ + void* result = 0; + + NotImplemented; + + return result; +} + +function +system_memory_set_protection_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_memory_free_sig(){ + NotImplemented; +} + +function +system_memory_annotation_sig(){ + Memory_Annotation result = {}; + + NotImplemented; + + return result; +} + +function +system_show_mouse_cursor_sig(){ + NotImplemented; +} + +function +system_set_fullscreen_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_is_fullscreen_sig(){ + b32 result = false; + + NotImplemented; + + return result; +} + +function +system_get_keyboard_modifiers_sig(){ + Input_Modifier_Set result = {}; + + NotImplemented; + + return result; +} diff --git a/platform_mac/mac_4ed_old.cpp b/platform_mac/mac_4ed_old.cpp index 77b111ec..81e7ffc3 100644 --- a/platform_mac/mac_4ed_old.cpp +++ b/platform_mac/mac_4ed_old.cpp @@ -782,7 +782,7 @@ osx_init(){ osxvars.input.first_step = true; // - // HACK(allen): + // HACK(allen): // Previously zipped stuff is here, it should be zipped in the new pattern now. // diff --git a/platform_mac/mac_4ed_old.m b/platform_mac/mac_4ed_old.m index 098da2fe..ebadc1ca 100644 --- a/platform_mac/mac_4ed_old.m +++ b/platform_mac/mac_4ed_old.m @@ -9,7 +9,6 @@ // TOP -#if 0 #define IS_OBJC_LAYER #include "4coder_base_types.h" @@ -24,8 +23,6 @@ #define external #include "osx_objective_c_to_cpp_links.h" -#endif - #include #import @@ -33,7 +30,7 @@ #import #import #import -#if 0 + #include #include #include @@ -817,9 +814,9 @@ osx_list_loadable_fonts(void){ NSString *font_n = fonts[i]; char *font_n_c = (char*)[font_n UTF8String]; NSFont *font = [font_manager - fontWithFamily:font_n - traits:NSUnboldFontMask|NSUnitalicFontMask - weight:5 + fontWithFamily:font_n + traits:NSUnboldFontMask|NSUnitalicFontMask + weight:5 size:12]; NSString *path = get_font_path(font); char *path_c = 0; @@ -843,10 +840,9 @@ OSX_Keyboard_Modifiers osx_get_modifiers(void){ return(osx_mods_nsevent_to_struct([NSEvent modifierFlags])); } -#endif + int main(int argc, char **argv){ -#if 0 memset(&osx_objc, 0, sizeof(osx_objc)); u32 clipboard_size = KB(16); @@ -891,7 +887,7 @@ main(int argc, char **argv){ [NSApp run]; } -#endif + return(0); } diff --git a/platform_mac/mac_objective_c_to_cpp_links.h b/platform_mac/mac_objective_c_to_cpp_links.h new file mode 100644 index 00000000..09605098 --- /dev/null +++ b/platform_mac/mac_objective_c_to_cpp_links.h @@ -0,0 +1,13 @@ +/* Types and functions for communication between C++ and Objective-C layers. */ + +#if !defined(MAC_OBJECTIVE_C_TO_CPP_LINKS_H) +#define MAC_OBJECTIVE_C_TO_CPP_LINKS_H + +// In C++ layer. +external void* +mac_init(); + +// In Objective-C layer. + +#endif + diff --git a/platform_mac/osx_objective_c_to_cpp_links.h b/platform_mac/osx_objective_c_to_cpp_links_old.h similarity index 100% rename from platform_mac/osx_objective_c_to_cpp_links.h rename to platform_mac/osx_objective_c_to_cpp_links_old.h From 57c0707284cb845773a06d1780db3e1f57fe89b4 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 25 Dec 2019 03:17:12 +0200 Subject: [PATCH 10/67] Implemented system_get_path. --- platform_mac/mac_4ed.cpp | 27 ++++- platform_mac/mac_4ed.mm | 16 +++ platform_mac/mac_4ed_functions.cpp | 128 +++++++++++++++----- platform_mac/mac_objective_c_to_cpp_links.h | 4 +- platform_win32/win32_4ed_functions.cpp | 4 +- 5 files changed, 140 insertions(+), 39 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index 7272be87..ec82755a 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -36,6 +36,11 @@ #include "4ed_search_list.cpp" +#include "mac_objective_c_to_cpp_links.h" + +#include +#include + //////////////////////////////// #define SLASH '\\' @@ -54,12 +59,14 @@ struct Mac_Vars { Thread_Context *tctx; Arena* frame_arena; + + String_Const_u8 binary_path; }; //////////////////////////////// -Mac_Vars global_mac_vars; -global Render_Target global_target; +Mac_Vars mac_vars; +global Render_Target target; //////////////////////////////// @@ -75,8 +82,8 @@ mac_init() { get_base_allocator_system(), get_base_allocator_system()); - block_zero_struct(&global_mac_vars); - global_mac_vars.tctx = &_tctx; + block_zero_struct(&mac_vars); + mac_vars.tctx = &_tctx; API_VTable_system system_vtable = {}; system_api_fill_vtable(&system_vtable); @@ -88,6 +95,14 @@ mac_init() { font_api_fill_vtable(&font_vtable); // NOTE(yuval): Memory - global_mac_vars.frame_arena = reserve_arena(global_mac_vars.tctx); - global_target.arena = make_arena_system(KB(256)); + mac_vars.frame_arena = reserve_arena(mac_vars.tctx); + target.arena = make_arena_system(KB(256)); + +#if 0 + mac_vars.cursor_show = MouseCursorShow_Always; + mac_vars.prev_cursor_show = MouseCursorShow_Always; + + dll_init_sentinel(&mac_vars.free_mac_objects); + dll_init_sentinel(&mac_vars.timer_objects); +#endif } \ No newline at end of file diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 538e2f4a..cdd47d24 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,6 +1,7 @@ /* Mac Objective C layer for 4coder */ #include "4coder_base_types.h" + #include "mac_objective_c_to_cpp_links.h" #undef function @@ -9,6 +10,13 @@ #undef external #include +#include // NOTE(yuval): Used for proc_pidpath + +#include +#include // NOTE(yuval): Used for getpid + +#define external extern "C" + @interface App_Delegate : NSObject @end @@ -33,6 +41,14 @@ } @end +external i32 +mac_get_binary_path(void *buffer, u32 size){ + pid_t pid = getpid(); + i32 bytes_read = proc_pidpath(pid, buffer, size); + + return bytes_read; +} + int main(int arg_count, char **args){ @autoreleasepool{ diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index 7288250c..e157019b 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -1,12 +1,47 @@ -/* General macOS Functions */ +/* macOS System/Graphics/Font API Implementations */ + +//////////////////////////////// function system_get_path_sig(){ String_Const_u8 result = {}; - NotImplemented; + switch (path_code){ + case SystemPath_CurrentDirectory: + { + char *working_dir = getcwd(NULL, 0); + u64 working_dir_length = cstring_length(working_dir); + + // TODO(yuval): Maybe use push_string_copy instead + u8 *out = push_array(arena, u8, working_dir_length); + block_copy(out, working_dir, working_dir_length); + + free(working_dir); + + result = SCu8(out, working_dir_length); + } break; + + case SystemPath_Binary: + { + local_persist b32 has_stashed_4ed_path = false; + if (!has_stashed_4ed_path){ + local_const i32 binary_path_capacity = KB(32); + u8 *memory = (u8*)system_memory_allocate(binary_path_capacity, string_u8_litexpr(file_name_line_number)); + i32 size = mac_get_binary_path(memory, binary_path_capacity); + Assert(size <= binary_path_capacity - 1); + + mac_vars.binary_path = SCu8(memory, size); + mac_vars.binary_path = string_remove_last_folder(mac_vars.binary_path); + mac_vars.binary_path.str[mac_vars.binary_path.size] = 0; + + has_stashed_4ed_path = true; + } + + result = push_string_copy(arena, mac_vars.binary_path); + } break; + } - return result; + return(result); } function @@ -15,7 +50,7 @@ system_get_canonical_sig(){ NotImplemented; - return result; + return(result); } function @@ -24,7 +59,7 @@ system_get_file_list_sig(){ NotImplemented; - return result; + return(result); } function @@ -33,7 +68,7 @@ system_quick_file_attributes_sig(){ NotImplemented; - return result; + return(result); } function @@ -42,7 +77,7 @@ system_load_handle_sig(){ NotImplemented; - return result; + return(result); } function @@ -51,7 +86,7 @@ system_load_attributes_sig(){ NotImplemented; - return result; + return(result); } function @@ -60,7 +95,7 @@ system_load_file_sig(){ NotImplemented; - return result; + return(result); } function @@ -69,7 +104,7 @@ system_load_close_sig(){ NotImplemented; - return result; + return(result); } function @@ -78,7 +113,7 @@ system_save_file_sig(){ NotImplemented; - return result; + return(result); } function @@ -87,7 +122,7 @@ system_load_library_sig(){ NotImplemented; - return result; + return(result); } function @@ -96,7 +131,7 @@ system_release_library_sig(){ NotImplemented; - return result; + return(result); } function @@ -105,7 +140,7 @@ system_get_proc_sig(){ NotImplemented; - return result; + return(result); } function @@ -114,7 +149,7 @@ system_now_time_sig(){ NotImplemented; - return result; + return(result); } function @@ -123,7 +158,7 @@ system_wake_up_timer_create_sig(){ NotImplemented; - return result; + return(result); } function @@ -157,7 +192,7 @@ system_cli_call_sig(){ NotImplemented; - return result; + return(result); } function @@ -171,7 +206,7 @@ system_cli_update_step_sig(){ NotImplemented; - return result; + return(result); } function @@ -180,7 +215,7 @@ system_cli_end_update_sig(){ NotImplemented; - return result; + return(result); } function @@ -194,7 +229,7 @@ system_get_screen_scale_factor_sig(){ NotImplemented; - return result; + return(result); } function @@ -203,7 +238,7 @@ system_thread_launch_sig(){ NotImplemented; - return result; + return(result); } function @@ -222,7 +257,7 @@ system_thread_get_id_sig(){ NotImplemented; - return result; + return(result); } function @@ -241,7 +276,7 @@ system_mutex_make_sig(){ NotImplemented; - return result; + return(result); } function @@ -265,7 +300,7 @@ system_condition_variable_make_sig(){ NotImplemented; - return result; + return(result); } function @@ -289,7 +324,7 @@ system_memory_allocate_sig(){ NotImplemented; - return result; + return(result); } function @@ -298,7 +333,7 @@ system_memory_set_protection_sig(){ NotImplemented; - return result; + return(result); } function @@ -312,7 +347,7 @@ system_memory_annotation_sig(){ NotImplemented; - return result; + return(result); } function @@ -326,7 +361,7 @@ system_set_fullscreen_sig(){ NotImplemented; - return result; + return(result); } function @@ -335,7 +370,7 @@ system_is_fullscreen_sig(){ NotImplemented; - return result; + return(result); } function @@ -344,5 +379,38 @@ system_get_keyboard_modifiers_sig(){ NotImplemented; - return result; + return(result); } + +//////////////////////////////// + +function +graphics_get_texture_sig(){ + u32 result = 0; + + NotImplemented; + + return(result); +} + +function +graphics_fill_texture_sig(){ + b32 result = false; + + NotImplemented; + + return(result); +} + +//////////////////////////////// + +function +font_make_face_sig(){ + Face* result = 0; + + NotImplemented; + + return(result); +} + +//////////////////////////////// \ No newline at end of file diff --git a/platform_mac/mac_objective_c_to_cpp_links.h b/platform_mac/mac_objective_c_to_cpp_links.h index 09605098..0dc4ae3a 100644 --- a/platform_mac/mac_objective_c_to_cpp_links.h +++ b/platform_mac/mac_objective_c_to_cpp_links.h @@ -4,10 +4,12 @@ #define MAC_OBJECTIVE_C_TO_CPP_LINKS_H // In C++ layer. -external void* +external void mac_init(); // In Objective-C layer. +external i32 +mac_get_binary_path(void* buffer, u32 size); #endif diff --git a/platform_win32/win32_4ed_functions.cpp b/platform_win32/win32_4ed_functions.cpp index 8e139ea1..5e7533b3 100644 --- a/platform_win32/win32_4ed_functions.cpp +++ b/platform_win32/win32_4ed_functions.cpp @@ -444,7 +444,7 @@ color_picker_hook(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam){ // Would it have killed you to update rgbResult continuously, or at least // provide a GetCurrentColor() call??? // - // Anyway, since the color picker doesn't tell us when the color is + // Anyway, since the color picker doesn't tell us when the color is // changed, what we do is watch for messages that repaint the color // swatch, which is dialog id 0x2c5, and then we sample it to see what // color it is. No, I'm not fucking kidding, that's what we do. @@ -533,7 +533,7 @@ internal system_open_color_picker_sig(){ // TODO(allen): review // NOTE(casey): Because this is going to be used by a semi-permanent thread, we need to - // copy it to system memory where it can live as long as it wants, no matter what we do + // copy it to system memory where it can live as long as it wants, no matter what we do // over here on the 4coder threads. Color_Picker *perm = (Color_Picker*)system_memory_allocate(sizeof(Color_Picker), string_u8_litexpr(file_name_line_number)); *perm = *picker; From 9b0708a40cf61ab7a718f65d7db6da025210ee6c Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 25 Dec 2019 23:31:21 +0200 Subject: [PATCH 11/67] Implemented system_get_canonical. --- platform_mac/mac_4ed.mm | 21 ++++++++++++++++++++- platform_mac/mac_4ed_functions.cpp | 5 +---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index cdd47d24..d3c4f601 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -12,7 +12,7 @@ #include // NOTE(yuval): Used for proc_pidpath -#include +#include // NOTE(yuval): Used for pid_t #include // NOTE(yuval): Used for getpid #define external extern "C" @@ -41,6 +41,21 @@ } @end +external String_Const_u8 +mac_standardize_path(Arena* arena, String_Const_u8 path){ + NSString *path_ns_str = + [[NSString alloc] initWithBytes:path.data length:path.size encoding:NSUTF8StringEncoding]; + + NSString *standardized_path_ns_str = [path_ns_str stringByStandardizingPath]; + String_Const_u8 standardized_path = SCu8([standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + + String_Const_u8 result = push_string_copy(arena, standardized_path); + + [path release]; + + return result; +} + external i32 mac_get_binary_path(void *buffer, u32 size){ pid_t pid = getpid(); @@ -52,6 +67,10 @@ mac_get_binary_path(void *buffer, u32 size){ int main(int arg_count, char **args){ @autoreleasepool{ + NSFileManager *fileManager = [[NSFileManager alloc] init]; + NSString *displayNameAtPath = [fileManager displayNameAtPath:@"build"]; + NSLog(@"Display Name: %@", displayNameAtPath); + // NOTE(yuval): Create NSApplication & Delegate NSApplication* app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index e157019b..bb385e2e 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -46,10 +46,7 @@ system_get_path_sig(){ function system_get_canonical_sig(){ - String_Const_u8 result = {}; - - NotImplemented; - + String_Const_u8 result = mac_standardize_path(arena, name); return(result); } From 2f9a4dbe3a6121a4015c19c4370cca05017f1827 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 26 Dec 2019 00:19:50 +0200 Subject: [PATCH 12/67] Fixed compilation errors and tested system_get_canonical. --- platform_mac/mac_4ed.cpp | 15 ++++++++++++++- platform_mac/mac_4ed.mm | 14 +++++--------- platform_mac/mac_objective_c_to_cpp_links.h | 13 +++++++++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index ec82755a..7b66f04d 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -48,6 +48,7 @@ #include "4coder_hash_functions.cpp" #include "4coder_system_allocator.cpp" +#include "4coder_malloc_allocator.cpp" #include "4coder_codepoint_map.cpp" #include "4ed_mem.cpp" @@ -74,8 +75,20 @@ global Render_Target target; //////////////////////////////// +external String_Const_u8 +mac_SCu8(u8* str, u64 size){ + String_Const_u8 result = SCu8(str, size); + return(result); +} + +external String_Const_u8 +mac_push_string_copy(Arena *arena, String_Const_u8 src){ + String_Const_u8 result = push_string_copy(arena, src); + return(result); +} + external void -mac_init() { +mac_init(){ // NOTE(yuval): Context Setup Thread_Context _tctx = {}; thread_ctx_init(&_tctx, ThreadKind_Main, diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index d3c4f601..a39fe56e 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -47,13 +47,13 @@ mac_standardize_path(Arena* arena, String_Const_u8 path){ [[NSString alloc] initWithBytes:path.data length:path.size encoding:NSUTF8StringEncoding]; NSString *standardized_path_ns_str = [path_ns_str stringByStandardizingPath]; - String_Const_u8 standardized_path = SCu8([standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + String_Const_u8 standardized_path = mac_SCu8((u8*)[standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); - String_Const_u8 result = push_string_copy(arena, standardized_path); + String_Const_u8 result = mac_push_string_copy(arena, standardized_path); - [path release]; + [path_ns_str release]; - return result; + return(result); } external i32 @@ -61,16 +61,12 @@ mac_get_binary_path(void *buffer, u32 size){ pid_t pid = getpid(); i32 bytes_read = proc_pidpath(pid, buffer, size); - return bytes_read; + return(bytes_read); } int main(int arg_count, char **args){ @autoreleasepool{ - NSFileManager *fileManager = [[NSFileManager alloc] init]; - NSString *displayNameAtPath = [fileManager displayNameAtPath:@"build"]; - NSLog(@"Display Name: %@", displayNameAtPath); - // NOTE(yuval): Create NSApplication & Delegate NSApplication* app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; diff --git a/platform_mac/mac_objective_c_to_cpp_links.h b/platform_mac/mac_objective_c_to_cpp_links.h index 0dc4ae3a..c5f4db62 100644 --- a/platform_mac/mac_objective_c_to_cpp_links.h +++ b/platform_mac/mac_objective_c_to_cpp_links.h @@ -3,13 +3,22 @@ #if !defined(MAC_OBJECTIVE_C_TO_CPP_LINKS_H) #define MAC_OBJECTIVE_C_TO_CPP_LINKS_H -// In C++ layer. +// In C++ layer +external String_Const_u8 +mac_SCu8(u8* str, u64 size); + +external String_Const_u8 +mac_push_string_copy(Arena *arena, String_Const_u8 src); + external void mac_init(); -// In Objective-C layer. +// In Objective-C layer external i32 mac_get_binary_path(void* buffer, u32 size); +external String_Const_u8 +mac_standardize_path(Arena* arena, String_Const_u8 path); + #endif From 131769223346d8471470580ac4471aa1bbe64fc8 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 26 Dec 2019 03:16:34 +0200 Subject: [PATCH 13/67] Work on system_get_file_list. --- platform_mac/mac_4ed.cpp | 10 +++- platform_mac/mac_4ed.mm | 21 +++++++ platform_mac/mac_4ed_functions.cpp | 92 +++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index 7b66f04d..f9f57fb6 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -38,8 +38,10 @@ #include "mac_objective_c_to_cpp_links.h" -#include -#include +#include // NOTE(yuval): Used for getcwd +#include // NOTE(yuval): Used for opendir, readdir + +#include // NOTE(yuval): Used for free //////////////////////////////// @@ -89,6 +91,10 @@ mac_push_string_copy(Arena *arena, String_Const_u8 src){ external void mac_init(){ + Arena test_arena = make_arena_malloc(); + system_get_file_list(&test_arena, + string_u8_litexpr("/Users/yuvaldolev/Desktop")); + // NOTE(yuval): Context Setup Thread_Context _tctx = {}; thread_ctx_init(&_tctx, ThreadKind_Main, diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index a39fe56e..ca147bb1 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -41,6 +41,27 @@ } @end +#if 0 +external File_List +mac_get_file_list(Arena* arena, String_Const_u8 directory){ + File_List result = {}; + + NSString *directory_ns_string = + [[NSString alloc] + initWithBytes:directory.data length:directory.size encoding:NSUTF8StringEncoding]; + + NSFileManager *file_manager = [NSFileManager defaultManager]; + NSDirectoryEnumerator *dirEnum = [file_manager enumeratorAtPath:directory_ns_string]; + + NSString *filename; + while ((filename = [dirEnum nextObject])){ + NSLog(filename); + } + + [directory_ns_string release]; +} +#endif + external String_Const_u8 mac_standardize_path(Arena* arena, String_Const_u8 path){ NSString *path_ns_str = diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index bb385e2e..1790e857 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -54,7 +54,97 @@ function system_get_file_list_sig(){ File_List result = {}; - NotImplemented; + u8* c_directory = push_array(arena, u8, directory.size + 1); + block_copy(c_directory, directory.str, directory.size); + c_directory[directory.size] = 0; + + DIR *dir = opendir((char*)c_directory); + if (dir){ + File_Info* first = 0; + File_Info* last = 0; + i32 count = 0; + + for (struct dirent *entry = readdir(dir); + entry; + entry = readdir(dir)){ + char *c_file_name = entry->d_name; + String_Const_u8 file_name = SCu8(c_file_name); + if (string_match(file_name, string_u8_litexpr(".")) || string_match(file_name, string_u8_litexpr(".."))){ + continue; + } + + File_Info* info = push_array(arena, File_Info, 1); + sll_queue_push(first, last, info); + count += 1; + + info->file_name = push_string_copy(arena, file_name); + //info->attributes.size = + + /*++file_count; + i32 size = 0; + for (; fname[size]; ++size); + character_count += size + 1;*/ + } + +#if 0 + i32 required_size = character_count + file_count * sizeof(File_Info); + if (file_list->block_size < required_size){ + system_memory_free(file_list->block, file_list->block_size); + file_list->block = system_memory_allocate(required_size); + file_list->block_size = required_size; + } + + file_list->infos = (File_Info*)file_list->block; + char *cursor = (char*)(file_list->infos + file_count); + + if (file_list->block != 0){ + rewinddir(d); + File_Info *info_ptr = file_list->infos; + for (struct dirent *entry = readdir(d); + entry != 0; + entry = readdir(d)){ + char *fname = entry->d_name; + if (match(fname, ".") || match(fname, "..")){ + continue; + } + char *cursor_start = cursor; + i32 length = copy_fast_unsafe_cc(cursor_start, fname); + cursor += length; + + if (entry->d_type == DT_LNK){ + struct stat st; + if (stat(entry->d_name, &st) != -1){ + info_ptr->folder = S_ISDIR(st.st_mode); + } + else{ + info_ptr->folder = false; + } + } + else{ + info_ptr->folder = (entry->d_type == DT_DIR); + } + + info_ptr->filename = cursor_start; + info_ptr->filename_len = length; + *cursor++ = 0; + ++info_ptr; + } + } + + file_list->count = file_count; + +#endif + + closedir(dir); + } + else{ + + /*system_memory_free(file_list->block, file_list->block_size); + file_list->block = 0; + file_list->block_size = 0; + file_list->infos = 0; + file_list->count = 0;*/ + } return(result); } From 3131e45c12db515b945472f826f259c6fb067f0d Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 26 Dec 2019 03:39:40 +0200 Subject: [PATCH 14/67] Errors regarding File_List which is used in mac_get_file_list (not implemented yet). --- platform_mac/mac_4ed.mm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index ca147bb1..badc89e2 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -2,6 +2,10 @@ #include "4coder_base_types.h" +#include "4coder_table.h" +#include "4coder_events.h" +#include "4coder_types.h" + #include "mac_objective_c_to_cpp_links.h" #undef function @@ -41,7 +45,6 @@ } @end -#if 0 external File_List mac_get_file_list(Arena* arena, String_Const_u8 directory){ File_List result = {}; @@ -60,7 +63,6 @@ mac_get_file_list(Arena* arena, String_Const_u8 directory){ [directory_ns_string release]; } -#endif external String_Const_u8 mac_standardize_path(Arena* arena, String_Const_u8 path){ From ea29a6e13ee7c4400989d778440c31f0ab2d5702 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 26 Dec 2019 17:54:06 +0200 Subject: [PATCH 15/67] Work on system_get_file_list. File attribute retrieval now works. --- platform_mac/mac_4ed.cpp | 7 +++- platform_mac/mac_4ed.mm | 6 +++- platform_mac/mac_4ed_functions.cpp | 50 +++++++++++++++++++++++++--- platform_unix/unix_4ed_functions.cpp | 2 +- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index f9f57fb6..f3c188d7 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -38,8 +38,13 @@ #include "mac_objective_c_to_cpp_links.h" +#include +#include + #include // NOTE(yuval): Used for getcwd #include // NOTE(yuval): Used for opendir, readdir +#include // NOTE(yuval): Used for struct stat +#include // NOTE(yuval): Used for stat #include // NOTE(yuval): Used for free @@ -95,6 +100,7 @@ mac_init(){ system_get_file_list(&test_arena, string_u8_litexpr("/Users/yuvaldolev/Desktop")); +#if 0 // NOTE(yuval): Context Setup Thread_Context _tctx = {}; thread_ctx_init(&_tctx, ThreadKind_Main, @@ -117,7 +123,6 @@ mac_init(){ mac_vars.frame_arena = reserve_arena(mac_vars.tctx); target.arena = make_arena_system(KB(256)); -#if 0 mac_vars.cursor_show = MouseCursorShow_Always; mac_vars.prev_cursor_show = MouseCursorShow_Always; diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index badc89e2..0e3e9485 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -2,9 +2,11 @@ #include "4coder_base_types.h" +#if 0 #include "4coder_table.h" #include "4coder_events.h" #include "4coder_types.h" +#endif #include "mac_objective_c_to_cpp_links.h" @@ -45,6 +47,7 @@ } @end +#if 0 external File_List mac_get_file_list(Arena* arena, String_Const_u8 directory){ File_List result = {}; @@ -58,11 +61,12 @@ mac_get_file_list(Arena* arena, String_Const_u8 directory){ NSString *filename; while ((filename = [dirEnum nextObject])){ - NSLog(filename); + NSLog(@"%@", filename); } [directory_ns_string release]; } +#endif external String_Const_u8 mac_standardize_path(Arena* arena, String_Const_u8 path){ diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index 1790e857..a61bca38 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -69,6 +69,7 @@ system_get_file_list_sig(){ entry = readdir(dir)){ char *c_file_name = entry->d_name; String_Const_u8 file_name = SCu8(c_file_name); + if (string_match(file_name, string_u8_litexpr(".")) || string_match(file_name, string_u8_litexpr(".."))){ continue; } @@ -78,12 +79,53 @@ system_get_file_list_sig(){ count += 1; info->file_name = push_string_copy(arena, file_name); - //info->attributes.size = + + // NOTE(yuval): Get file attributes + { + Temp_Memory temp = begin_temp(arena); + + b32 append_slash = false; + u64 file_path_size = directory.size + file_name.size; + if (string_get_character(directory, directory.size - 1) != '/') { + append_slash = true; + file_path_size += 1; + } + + char* file_path = push_array(arena, char, file_path_size + 1); + char* file_path_at = file_path; + + block_copy(file_path_at, directory.str, directory.size); + file_path_at += directory.size; + + if (append_slash) { + *file_path_at = '/'; + file_path_at += 1; + } + + block_copy(file_path_at, file_name.str, file_name.size); + file_path_at += file_name.size; + + *file_path_at = 0; + + printf("File Path: %s ", file_path); + + struct stat file_stat; + if (stat(file_path, &file_stat) == 0){ + info->attributes.size = file_stat.st_size; + info->attributes.last_write_time = ; + info->attributes.flags = ; + } else { + char* error_message = strerror(errno); + printf("ERROR: stat failed with error message '%s'!\n", error_message); + } + + end_temp(temp); + } /*++file_count; - i32 size = 0; - for (; fname[size]; ++size); - character_count += size + 1;*/ +i32 size = 0; +for (; fname[size]; ++size); +character_count += size + 1;*/ } #if 0 diff --git a/platform_unix/unix_4ed_functions.cpp b/platform_unix/unix_4ed_functions.cpp index 75e121fb..d6fe65b7 100644 --- a/platform_unix/unix_4ed_functions.cpp +++ b/platform_unix/unix_4ed_functions.cpp @@ -64,7 +64,7 @@ Sys_Memory_Allocate_Sig(system_memory_allocate){ return(result); } -internal +internal Sys_Memory_Set_Protection_Sig(system_memory_set_protection){ bool32 result = true; From 9c3a2d95503479aa8a6d8ea63cfe2e73a578f4ee Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 26 Dec 2019 21:30:13 +0200 Subject: [PATCH 16/67] Finished implmenting system_get_file_list. --- platform_mac/mac_4ed.cpp | 17 ++++-- platform_mac/mac_4ed_functions.cpp | 89 +++++++----------------------- 2 files changed, 31 insertions(+), 75 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index f3c188d7..7a3acc4e 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -38,9 +38,6 @@ #include "mac_objective_c_to_cpp_links.h" -#include -#include - #include // NOTE(yuval): Used for getcwd #include // NOTE(yuval): Used for opendir, readdir #include // NOTE(yuval): Used for struct stat @@ -97,8 +94,18 @@ mac_push_string_copy(Arena *arena, String_Const_u8 src){ external void mac_init(){ Arena test_arena = make_arena_malloc(); - system_get_file_list(&test_arena, - string_u8_litexpr("/Users/yuvaldolev/Desktop")); + File_List list = system_get_file_list(&test_arena, + string_u8_litexpr("/Users/yuvaldolev/Desktop")); + + for (u32 index = 0; index < list.count; ++index) { + File_Info* info = list.infos[index]; + + printf("File_Info{file_name:'%.*s', " + "attributes:{size:%llu, last_write_time:%llu, flags:{IsDirectory:%d}}}\n", + (i32)info->file_name.size, info->file_name.str, + info->attributes.size, info->attributes.last_write_time, + ((info->attributes.flags & FileAttribute_IsDirectory) != 0)); + } #if 0 // NOTE(yuval): Context Setup diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index a61bca38..3047ba18 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -86,7 +86,7 @@ system_get_file_list_sig(){ b32 append_slash = false; u64 file_path_size = directory.size + file_name.size; - if (string_get_character(directory, directory.size - 1) != '/') { + if (string_get_character(directory, directory.size - 1) != '/'){ append_slash = true; file_path_size += 1; } @@ -97,7 +97,7 @@ system_get_file_list_sig(){ block_copy(file_path_at, directory.str, directory.size); file_path_at += directory.size; - if (append_slash) { + if (append_slash){ *file_path_at = '/'; file_path_at += 1; } @@ -107,85 +107,34 @@ system_get_file_list_sig(){ *file_path_at = 0; - printf("File Path: %s ", file_path); - struct stat file_stat; if (stat(file_path, &file_stat) == 0){ info->attributes.size = file_stat.st_size; - info->attributes.last_write_time = ; - info->attributes.flags = ; - } else { - char* error_message = strerror(errno); - printf("ERROR: stat failed with error message '%s'!\n", error_message); + info->attributes.last_write_time = file_stat.st_mtimespec.tv_sec; + info->attributes.flags = 0; + + if (S_ISDIR(file_stat.st_mode)) { + info->attributes.flags |= FileAttribute_IsDirectory; + } + } end_temp(temp); } - - /*++file_count; -i32 size = 0; -for (; fname[size]; ++size); -character_count += size + 1;*/ } -#if 0 - i32 required_size = character_count + file_count * sizeof(File_Info); - if (file_list->block_size < required_size){ - system_memory_free(file_list->block, file_list->block_size); - file_list->block = system_memory_allocate(required_size); - file_list->block_size = required_size; - } - - file_list->infos = (File_Info*)file_list->block; - char *cursor = (char*)(file_list->infos + file_count); - - if (file_list->block != 0){ - rewinddir(d); - File_Info *info_ptr = file_list->infos; - for (struct dirent *entry = readdir(d); - entry != 0; - entry = readdir(d)){ - char *fname = entry->d_name; - if (match(fname, ".") || match(fname, "..")){ - continue; - } - char *cursor_start = cursor; - i32 length = copy_fast_unsafe_cc(cursor_start, fname); - cursor += length; - - if (entry->d_type == DT_LNK){ - struct stat st; - if (stat(entry->d_name, &st) != -1){ - info_ptr->folder = S_ISDIR(st.st_mode); - } - else{ - info_ptr->folder = false; - } - } - else{ - info_ptr->folder = (entry->d_type == DT_DIR); - } - - info_ptr->filename = cursor_start; - info_ptr->filename_len = length; - *cursor++ = 0; - ++info_ptr; - } - } - - file_list->count = file_count; - -#endif - closedir(dir); - } - else{ - /*system_memory_free(file_list->block, file_list->block_size); - file_list->block = 0; - file_list->block_size = 0; - file_list->infos = 0; - file_list->count = 0;*/ + result.infos = push_array(arena, File_Info*, count); + result.count = count; + + i32 index = 0; + for (File_Info* node = first; + node != 0; + node = node->next){ + result.infos[index] = node; + index += 1; + } } return(result); From 074400495f2fc73c1df68e4d32b656eb75c11628 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 27 Dec 2019 00:16:15 +0200 Subject: [PATCH 17/67] Implemented all system file handling functions. --- platform_mac/mac_4ed.cpp | 6 +- platform_mac/mac_4ed_functions.cpp | 120 ++++++++++++++++---- platform_mac/mac_objective_c_to_cpp_links.h | 6 +- 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index 7a3acc4e..000d4336 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -38,10 +38,12 @@ #include "mac_objective_c_to_cpp_links.h" -#include // NOTE(yuval): Used for getcwd #include // NOTE(yuval): Used for opendir, readdir -#include // NOTE(yuval): Used for struct stat +#include // NOTE(yuval): Used for errno +#include // NOTE(yuval): Used for open +#include // NOTE(yuval): Used for getcwd, read, write #include // NOTE(yuval): Used for stat +#include // NOTE(yuval): Used for struct stat #include // NOTE(yuval): Used for free diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index 3047ba18..e0b3d198 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -50,6 +50,44 @@ system_get_canonical_sig(){ return(result); } +function File_Attributes +mac_get_file_attributes(struct stat file_stat) { + File_Attributes result; + result.size = file_stat.st_size; + result.last_write_time = file_stat.st_mtimespec.tv_sec; + + result.flags = 0; + if (S_ISDIR(file_stat.st_mode)) { + result.flags |= FileAttribute_IsDirectory; + } + + return(result); +} + +function inline File_Attributes +mac_file_attributes_from_path(char* path) { + File_Attributes result = {}; + + struct stat file_stat; + if (stat(path, &file_stat) == 0){ + result = mac_get_file_attributes(file_stat); + } + + return(result); +} + +function inline File_Attributes +mac_file_attributes_from_fd(i32 fd) { + File_Attributes result = {}; + + struct stat file_stat; + if (fstat(fd, &file_stat) == 0){ + result = mac_get_file_attributes(file_stat); + } + + return(result); +} + function system_get_file_list_sig(){ File_List result = {}; @@ -107,17 +145,7 @@ system_get_file_list_sig(){ *file_path_at = 0; - struct stat file_stat; - if (stat(file_path, &file_stat) == 0){ - info->attributes.size = file_stat.st_size; - info->attributes.last_write_time = file_stat.st_mtimespec.tv_sec; - info->attributes.flags = 0; - - if (S_ISDIR(file_stat.st_mode)) { - info->attributes.flags |= FileAttribute_IsDirectory; - } - - } + info->attributes = mac_file_attributes_from_path(file_path); end_temp(temp); } @@ -142,9 +170,15 @@ system_get_file_list_sig(){ function system_quick_file_attributes_sig(){ - File_Attributes result = {}; + Temp_Memory temp = begin_temp(scratch); - NotImplemented; + char* c_file_name = push_array(scratch, char, file_name.size + 1); + block_copy(c_file_name, file_name.str, file_name.size); + c_file_name[file_name.size] = 0; + + File_Attributes result = mac_file_attributes_from_path(c_file_name); + + end_temp(temp); return(result); } @@ -153,34 +187,53 @@ function system_load_handle_sig(){ b32 result = false; - NotImplemented; + i32 fd = open(file_name, O_RDONLY); + if ((fd != -1) && (fd != 0)) { + *(i32*)out = fd; + result = true; + } return(result); } function system_load_attributes_sig(){ - File_Attributes result = {}; - - NotImplemented; + i32 fd = *(i32*)(&handle); + File_Attributes result = mac_file_attributes_from_fd(fd); return(result); } function system_load_file_sig(){ - b32 result = false; + i32 fd = *(i32*)(&handle); - NotImplemented; + do{ + ssize_t bytes_read = read(fd, buffer, size); + if (bytes_read == -1){ + if (errno != EINTR){ + // NOTE(yuval): An error occured while reading from the file descriptor + break; + } + } else{ + size -= bytes_read; + buffer += bytes_read; + } + } while (size > 0); + b32 result = (size == 0); return(result); } function system_load_close_sig(){ - b32 result = false; + b32 result = true; - NotImplemented; + i32 fd = *(i32*)(&handle); + if (close(fd) == -1){ + // NOTE(yuval): An error occured while close the file descriptor + result = false; + } return(result); } @@ -189,7 +242,30 @@ function system_save_file_sig(){ File_Attributes result = {}; - NotImplemented; + i32 fd = open(file_name, O_WRONLY | O_TRUNC | O_CREAT, 00640); + if (fd != -1) { + u8* data_str = data.str; + u64 data_size = data.size; + + do{ + ssize_t bytes_written = write(fd, data.str, data.size); + if (bytes_written == -1){ + if (errno != EINTR){ + // NOTE(yuval): An error occured while writing to the file descriptor + break; + } + } else{ + data.size -= bytes_written; + data.str += bytes_written; + } + } while (data.size > 0); + + if (data.size == 0) { + result = mac_file_attributes_from_fd(fd); + } + + close(fd); + } return(result); } diff --git a/platform_mac/mac_objective_c_to_cpp_links.h b/platform_mac/mac_objective_c_to_cpp_links.h index c5f4db62..ab043fc1 100644 --- a/platform_mac/mac_objective_c_to_cpp_links.h +++ b/platform_mac/mac_objective_c_to_cpp_links.h @@ -14,11 +14,11 @@ external void mac_init(); // In Objective-C layer -external i32 -mac_get_binary_path(void* buffer, u32 size); - external String_Const_u8 mac_standardize_path(Arena* arena, String_Const_u8 path); +external i32 +mac_get_binary_path(void* buffer, u32 size); + #endif From 0a5f6d51f651a0914872b1dac7791c9591b420e2 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Thu, 26 Dec 2019 17:12:13 -0800 Subject: [PATCH 18/67] removing dead min/max macros --- custom/4coder_base_types.h | 2 - custom/generated/command_metadata.h | 458 +++++++++--------- .../Resources/DWARF/metadata_generator | Bin 136740 -> 138399 bytes 3 files changed, 229 insertions(+), 231 deletions(-) diff --git a/custom/4coder_base_types.h b/custom/4coder_base_types.h index 358485d9..20517f7f 100644 --- a/custom/4coder_base_types.h +++ b/custom/4coder_base_types.h @@ -291,8 +291,6 @@ enum{ #define Max(a,b) (((a)>(b))?(a):(b)) #define Min(a,b) (((a)<(b))?(a):(b)) -#define max(a,b) (((a)>(b))?(a):(b)) -#define min(a,b) (((a)<(b))?(a):(b)) #define clamp_top(a,b) Min(a,b) #define clamp_bot(a,b) Max(a,b) #define clamp_(a,x,b) ((a>x)?a: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 HK1Tl^sgkr2 literal 136740 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 Date: Thu, 26 Dec 2019 19:47:12 -0800 Subject: [PATCH 19/67] Get 4coder_types.h to work with a dumb hack for now --- platform_mac/mac_4ed.cpp | 4 ++-- platform_mac/mac_4ed.mm | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index 000d4336..b2636154 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -49,8 +49,8 @@ //////////////////////////////// -#define SLASH '\\' -#define DLL "dll" +#define SLASH '/' +#define DLL "so" #include "4coder_hash_functions.cpp" #include "4coder_system_allocator.cpp" diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 0e3e9485..c2519d04 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -2,10 +2,15 @@ #include "4coder_base_types.h" -#if 0 +#if 1 #include "4coder_table.h" #include "4coder_events.h" +// NOTE(allen): This is a very unfortunate hack, but hopefully there will never be a need to use the Marker +// type in the platform layer. If that changes then instead change the name of Marker and make a transition +// macro that is only included in custom code. +#define Marker Marker__SAVE_THIS_IDENTIFIER #include "4coder_types.h" +#undef Marker #endif #include "mac_objective_c_to_cpp_links.h" From 874024f8fb5f7eeabaec5cb556baff415bda9018 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 27 Dec 2019 23:30:46 +0200 Subject: [PATCH 20/67] Added section comments to mac_4ed_functions.cpp --- platform_mac/mac_4ed_functions.cpp | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.cpp index e0b3d198..2c22135e 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.cpp @@ -1,5 +1,9 @@ /* macOS System/Graphics/Font API Implementations */ +////////////////////// +// System API // +////////////////////// + //////////////////////////////// function @@ -50,6 +54,8 @@ system_get_canonical_sig(){ return(result); } +//////////////////////////////// + function File_Attributes mac_get_file_attributes(struct stat file_stat) { File_Attributes result; @@ -270,6 +276,8 @@ system_save_file_sig(){ return(result); } +//////////////////////////////// + function system_load_library_sig(){ b32 result = false; @@ -297,6 +305,8 @@ system_get_proc_sig(){ return(result); } +//////////////////////////////// + function system_now_time_sig(){ u64 result = 0; @@ -335,11 +345,15 @@ system_sleep_sig(){ NotImplemented; } +//////////////////////////////// + function system_post_clipboard_sig(){ NotImplemented; } +//////////////////////////////// + function system_cli_call_sig(){ b32 result = false; @@ -372,6 +386,8 @@ system_cli_end_update_sig(){ return(result); } +//////////////////////////////// + function system_open_color_picker_sig(){ NotImplemented; @@ -386,6 +402,8 @@ system_get_screen_scale_factor_sig(){ return(result); } +//////////////////////////////// + function system_thread_launch_sig(){ System_Thread result = {}; @@ -472,6 +490,8 @@ system_condition_variable_free_sig(){ NotImplemented; } +//////////////////////////////// + function system_memory_allocate_sig(){ void* result = 0; @@ -504,6 +524,8 @@ system_memory_annotation_sig(){ return(result); } +//////////////////////////////// + function system_show_mouse_cursor_sig(){ NotImplemented; @@ -538,6 +560,12 @@ system_get_keyboard_modifiers_sig(){ //////////////////////////////// +//////////////////////// +// Graphics API // +//////////////////////// + +//////////////////////////////// + function graphics_get_texture_sig(){ u32 result = 0; @@ -558,6 +586,12 @@ graphics_fill_texture_sig(){ //////////////////////////////// +//////////////////// +// Font API // +//////////////////// + +//////////////////////////////// + function font_make_face_sig(){ Face* result = 0; From da150cd322a7804a5789524a8905018590eed5ee Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 27 Dec 2019 23:59:59 +0200 Subject: [PATCH 22/67] Merged mac_4ed.cpp into mac_4ed.mm so now we have only 1 main file for the macOS platform layer. --- bin/4ed_build.cpp | 2 +- custom/generated/command_metadata.h | 458 +++++++++--------- .../Resources/DWARF/metadata_generator | Bin 138399 -> 136740 bytes platform_mac/mac_4ed.cpp | 141 ------ platform_mac/mac_4ed.mm | 167 +++++-- ...4ed_functions.cpp => mac_4ed_functions.mm} | 15 +- 6 files changed, 362 insertions(+), 421 deletions(-) delete mode 100644 platform_mac/mac_4ed.cpp rename platform_mac/{mac_4ed_functions.cpp => mac_4ed_functions.mm} (94%) diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index f15d0040..146169fb 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -94,7 +94,7 @@ char *includes[] = { "custom", FOREIGN "/freetype2", 0, }; char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", "platform_mac/mac_4ed.cpp", 0 }; +char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", 0 }; char **platform_layers[Platform_COUNT] = { windows_platform_layer, diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index d19e25ba..46c04cd1 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -251,235 +251,235 @@ i32 source_name_len; i32 line_number; }; static Command_Metadata fcoder_metacmd_table[229] = { -{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 409 }, -{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "/Users/allenwebster/4ed/code/custom/4coder_auto_indent.cpp", 58, 375 }, -{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "/Users/allenwebster/4ed/code/custom/4coder_auto_indent.cpp", 58, 385 }, -{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "/Users/allenwebster/4ed/code/custom/4coder_auto_indent.cpp", 58, 366 }, -{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 154 }, -{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 96 }, -{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 613 }, -{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "/Users/allenwebster/4ed/code/custom/4coder_build_commands.cpp", 61, 165 }, -{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "/Users/allenwebster/4ed/code/custom/4coder_build_commands.cpp", 61, 128 }, -{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 197 }, -{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 284 }, -{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 290 }, -{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "/Users/allenwebster/4ed/code/custom/4coder_build_commands.cpp", 61, 186 }, -{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 578 }, -{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 480 }, -{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 233 }, -{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 223 }, -{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 243 }, -{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 255 }, -{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 842 }, -{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "/Users/allenwebster/4ed/code/custom/4coder_build_commands.cpp", 61, 180 }, -{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 621 }, -{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "/Users/allenwebster/4ed/code/custom/4coder_docs.cpp", 51, 190 }, -{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 668 }, -{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 125 }, -{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 149 }, -{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 19 }, -{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 124 }, -{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "/Users/allenwebster/4ed/code/custom/4coder_docs.cpp", 51, 175 }, -{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 28 }, -{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 684 }, -{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1798 }, -{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "/Users/allenwebster/4ed/code/custom/4coder_default_hooks.cpp", 60, 7 }, -{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "/Users/allenwebster/4ed/code/custom/4coder_default_hooks.cpp", 60, 23 }, -{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "/Users/allenwebster/4ed/code/custom/4coder_default_hooks.cpp", 60, 57 }, -{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 162 }, -{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 79 }, -{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 112 }, -{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1221 }, -{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1396 }, -{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 134 }, -{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1382 }, -{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "/Users/allenwebster/4ed/code/custom/4coder_cli_command.cpp", 58, 22 }, -{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "/Users/allenwebster/4ed/code/custom/4coder_cli_command.cpp", 58, 7 }, -{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 740 }, -{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2184 }, -{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2192 }, -{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 523 }, -{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 540 }, -{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 346 }, -{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 373 }, -{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 748 }, -{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 462 }, -{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 492 }, -{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 479 }, -{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 509 }, -{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 651 }, -{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 637 }, -{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "/Users/allenwebster/4ed/code/custom/4coder_tutorial.cpp", 55, 869 }, -{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 70 }, -{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 562 }, -{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "/Users/allenwebster/4ed/code/custom/4coder_jump_sticky.cpp", 58, 579 }, -{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 673 }, -{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 515 }, -{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 597 }, -{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 634 }, -{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 563 }, -{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 505 }, -{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "/Users/allenwebster/4ed/code/custom/4coder_code_index_listers.cpp", 65, 12 }, -{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "/Users/allenwebster/4ed/code/custom/4coder_keyboard_macro.cpp", 61, 57 }, -{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "/Users/allenwebster/4ed/code/custom/4coder_keyboard_macro.cpp", 61, 80 }, -{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "/Users/allenwebster/4ed/code/custom/4coder_keyboard_macro.cpp", 61, 44 }, -{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1542 }, -{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "/Users/allenwebster/4ed/code/custom/4coder_tutorial.cpp", 55, 9 }, -{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 211 }, -{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "/Users/allenwebster/4ed/code/custom/4coder_function_list.cpp", 60, 295 }, -{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "/Users/allenwebster/4ed/code/custom/4coder_function_list.cpp", 60, 301 }, -{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "/Users/allenwebster/4ed/code/custom/4coder_function_list.cpp", 60, 267 }, -{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "/Users/allenwebster/4ed/code/custom/4coder_function_list.cpp", 60, 277 }, -{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 162 }, -{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 174 }, -{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 186 }, -{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 192 }, -{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 198 }, -{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 204 }, -{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 210 }, -{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 218 }, -{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 168 }, -{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 180 }, -{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 862 }, -{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 457 }, -{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 469 }, -{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1336 }, -{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 44 }, -{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 237 }, -{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 249 }, -{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 29 }, -{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 231 }, -{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/allenwebster/4ed/code/custom/4coder_miblo_numbers.cpp", 60, 243 }, -{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 695 }, -{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 265 }, -{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 338 }, -{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 350 }, -{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 356 }, -{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 409 }, -{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 433 }, -{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 421 }, -{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 439 }, -{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 516 }, -{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 530 }, -{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 488 }, -{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 473 }, -{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 502 }, -{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1376 }, -{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1370 }, -{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 447 }, -{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 509 }, -{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 523 }, -{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 481 }, -{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 465 }, -{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 495 }, -{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 332 }, -{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 344 }, -{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 403 }, -{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 427 }, -{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 415 }, -{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 848 }, -{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 854 }, -{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1461 }, -{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1792 }, -{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 46 }, -{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 62 }, -{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 54 }, -{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1493 }, -{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 310 }, -{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 300 }, -{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 374 }, -{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 366 }, -{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 39 }, -{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 110 }, -{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 71 }, -{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "/Users/allenwebster/4ed/code/custom/4coder_clipboard.cpp", 56, 117 }, -{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 106 }, -{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "/Users/allenwebster/4ed/code/custom/4coder_profile.cpp", 54, 226 }, -{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "/Users/allenwebster/4ed/code/custom/4coder_profile.cpp", 54, 219 }, -{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "/Users/allenwebster/4ed/code/custom/4coder_profile.cpp", 54, 212 }, -{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "/Users/allenwebster/4ed/code/custom/4coder_profile_inspect.cpp", 62, 886 }, -{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 1289 }, -{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 870 }, -{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 896 }, -{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1149 }, -{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1170 }, -{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1186 }, -{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1631 }, -{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1716 }, -{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1298 }, -{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1560 }, -{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1059 }, -{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1050 }, -{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1041 }, -{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 982 }, -{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 994 }, -{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1550 }, -{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 382 }, -{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1265 }, -{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 976 }, -{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 988 }, -{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2172 }, -{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2160 }, -{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2178 }, -{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "/Users/allenwebster/4ed/code/custom/4coder_helper.cpp", 53, 2166 }, -{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 539 }, -{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 57 }, -{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 66 }, -{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 82 }, -{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 99 }, -{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 27 }, -{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "/Users/allenwebster/4ed/code/custom/4coder_scope_commands.cpp", 61, 39 }, -{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "/Users/allenwebster/4ed/code/custom/4coder_eol.cpp", 50, 125 }, -{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "/Users/allenwebster/4ed/code/custom/4coder_eol.cpp", 50, 112 }, -{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "/Users/allenwebster/4ed/code/custom/4coder_eol.cpp", 50, 86 }, -{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "/Users/allenwebster/4ed/code/custom/4coder_eol.cpp", 50, 99 }, -{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 115 }, -{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 427 }, -{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 421 }, -{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 1237 }, -{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 1249 }, -{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 1243 }, -{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "/Users/allenwebster/4ed/code/custom/4coder_project_commands.cpp", 63, 1230 }, -{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 644 }, -{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 630 }, -{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "/Users/allenwebster/4ed/code/custom/4coder_log_parser.cpp", 57, 994 }, -{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 179 }, -{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 187 }, -{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 237 }, -{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 403 }, -{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1518 }, -{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "/Users/allenwebster/4ed/code/custom/4coder_lists.cpp", 52, 692 }, -{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 565 }, -{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 552 }, -{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 658 }, -{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 667 }, -{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 451 }, -{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 439 }, -{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 433 }, -{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 721 }, -{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 727 }, -{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 415 }, -{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "/Users/allenwebster/4ed/code/custom/4coder_default_framework.cpp", 64, 445 }, -{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 712 }, -{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "/Users/allenwebster/4ed/code/custom/4coder_code_index.cpp", 57, 1160 }, -{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "/Users/allenwebster/4ed/code/custom/4coder_tutorial.cpp", 55, 20 }, -{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "/Users/allenwebster/4ed/code/custom/4coder_tutorial.cpp", 55, 34 }, -{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 137 }, -{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1618 }, -{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1645 }, -{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 1506 }, -{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "/Users/allenwebster/4ed/code/custom/4coder_jump_lister.cpp", 58, 59 }, -{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 392 }, -{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "/Users/allenwebster/4ed/code/custom/4coder_search.cpp", 53, 639 }, -{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 94 }, -{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 82 }, -{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 88 }, -{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 67 }, -{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "/Users/allenwebster/4ed/code/custom/4coder_auto_indent.cpp", 58, 395 }, -{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 59 }, -{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 76 }, -{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "/Users/allenwebster/4ed/code/custom/4coder_base_commands.cpp", 60, 73 }, -{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "/Users/allenwebster/4ed/code/custom/4coder_combined_write_commands.cpp", 70, 100 }, +{ PROC_LINKS(allow_mouse, 0), false, "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 409 }, +{ PROC_LINKS(auto_indent_line_at_cursor, 0), false, "auto_indent_line_at_cursor", 26, "Auto-indents the line on which the cursor sits.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 375 }, +{ PROC_LINKS(auto_indent_range, 0), false, "auto_indent_range", 17, "Auto-indents the range between the cursor and the mark.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 385 }, +{ PROC_LINKS(auto_indent_whole_file, 0), false, "auto_indent_whole_file", 22, "Audo-indents the entire current buffer.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 366 }, +{ PROC_LINKS(backspace_alpha_numeric_boundary, 0), false, "backspace_alpha_numeric_boundary", 32, "Delete characters between the cursor position and the first alphanumeric boundary to the left.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 154 }, +{ PROC_LINKS(backspace_char, 0), false, "backspace_char", 14, "Deletes the character to the left of the cursor.", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 96 }, +{ PROC_LINKS(basic_change_active_panel, 0), false, "basic_change_active_panel", 25, "Change the currently active panel, moving to the panel with the next highest view_id. Will not skipe the build panel if it is open.", 132, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 613 }, +{ PROC_LINKS(build_in_build_panel, 0), false, "build_in_build_panel", 20, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.", 230, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 165 }, +{ PROC_LINKS(build_search, 0), false, "build_search", 12, "Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.", 153, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 128 }, +{ PROC_LINKS(center_view, 0), false, "center_view", 11, "Centers the view vertically on the line on which the cursor sits.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 197 }, +{ PROC_LINKS(change_active_panel, 0), false, "change_active_panel", 19, "Change the currently active panel, moving to the panel with the next highest view_id.", 85, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 284 }, +{ PROC_LINKS(change_active_panel_backwards, 0), false, "change_active_panel_backwards", 29, "Change the currently active panel, moving to the panel with the next lowest view_id.", 84, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 290 }, +{ PROC_LINKS(change_to_build_panel, 0), false, "change_to_build_panel", 21, "If the special build panel is open, makes the build panel the active panel.", 75, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 186 }, +{ PROC_LINKS(clean_all_lines, 0), false, "clean_all_lines", 15, "Removes trailing whitespace from all lines in the current buffer.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 578 }, +{ PROC_LINKS(clear_all_themes, 0), false, "clear_all_themes", 16, "Clear the theme list", 20, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 480 }, +{ PROC_LINKS(click_set_cursor, 0), false, "click_set_cursor", 16, "Sets the cursor position to the mouse position.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 233 }, +{ PROC_LINKS(click_set_cursor_and_mark, 0), false, "click_set_cursor_and_mark", 25, "Sets the cursor position and mark to the mouse position.", 56, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 223 }, +{ PROC_LINKS(click_set_cursor_if_lbutton, 0), false, "click_set_cursor_if_lbutton", 27, "If the mouse left button is pressed, sets the cursor position to the mouse position.", 84, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 243 }, +{ PROC_LINKS(click_set_mark, 0), false, "click_set_mark", 14, "Sets the mark position to the mouse position.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 255 }, +{ PROC_LINKS(close_all_code, 0), false, "close_all_code", 14, "Closes any buffer with a filename ending with an extension configured to be recognized as a code file type.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 842 }, +{ PROC_LINKS(close_build_panel, 0), false, "close_build_panel", 17, "If the special build panel is open, closes it.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_build_commands.cpp", 59, 180 }, +{ PROC_LINKS(close_panel, 0), false, "close_panel", 11, "Closes the currently active panel if it is not the only panel open.", 67, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 621 }, +{ PROC_LINKS(command_documentation, 0), true, "command_documentation", 21, "Prompts the user to select a command then loads a doc buffer for that item", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_docs.cpp", 49, 190 }, +{ PROC_LINKS(command_lister, 0), true, "command_lister", 14, "Opens an interactive list of all registered commands.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 668 }, +{ PROC_LINKS(comment_line, 0), false, "comment_line", 12, "Insert '//' at the beginning of the line after leading whitespace.", 66, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 125 }, +{ PROC_LINKS(comment_line_toggle, 0), false, "comment_line_toggle", 19, "Turns uncommented lines into commented lines and vice versa for comments starting with '//'.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 149 }, +{ PROC_LINKS(copy, 0), false, "copy", 4, "Copy the text in the range from the cursor to the mark onto the clipboard.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 19 }, +{ PROC_LINKS(cursor_mark_swap, 0), false, "cursor_mark_swap", 16, "Swaps the position of the cursor and the mark.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 124 }, +{ PROC_LINKS(custom_api_documentation, 0), true, "custom_api_documentation", 24, "Prompts the user to select a Custom API item then loads a doc buffer for that item", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_docs.cpp", 49, 175 }, +{ PROC_LINKS(cut, 0), false, "cut", 3, "Cut the text in the range from the cursor to the mark onto the clipboard.", 73, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 28 }, +{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 684 }, +{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1798 }, +{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 7 }, +{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 23 }, +{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_default_hooks.cpp", 58, 57 }, +{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 162 }, +{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 79 }, +{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 112 }, +{ PROC_LINKS(delete_file_query, 0), false, "delete_file_query", 17, "Deletes the file of the current buffer if 4coder has the appropriate access rights. Will ask the user for confirmation first.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1221 }, +{ PROC_LINKS(delete_line, 0), false, "delete_line", 11, "Delete the line the on which the cursor sits.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1396 }, +{ PROC_LINKS(delete_range, 0), false, "delete_range", 12, "Deletes the text in the range between the cursor and the mark.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 134 }, +{ PROC_LINKS(duplicate_line, 0), false, "duplicate_line", 14, "Create a copy of the line on which the cursor sits.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1382 }, +{ PROC_LINKS(execute_any_cli, 0), false, "execute_any_cli", 15, "Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer.", 133, "/Users/yuvaldolev/4ed/code/custom/4coder_cli_command.cpp", 56, 22 }, +{ PROC_LINKS(execute_previous_cli, 0), false, "execute_previous_cli", 20, "If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.", 126, "/Users/yuvaldolev/4ed/code/custom/4coder_cli_command.cpp", 56, 7 }, +{ PROC_LINKS(exit_4coder, 0), false, "exit_4coder", 11, "Attempts to close 4coder.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 740 }, +{ PROC_LINKS(goto_beginning_of_file, 0), false, "goto_beginning_of_file", 22, "Sets the cursor to the beginning of the file.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2184 }, +{ PROC_LINKS(goto_end_of_file, 0), false, "goto_end_of_file", 16, "Sets the cursor to the end of the file.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2192 }, +{ PROC_LINKS(goto_first_jump, 0), false, "goto_first_jump", 15, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 523 }, +{ PROC_LINKS(goto_first_jump_same_panel_sticky, 0), false, "goto_first_jump_same_panel_sticky", 33, "If a buffer containing jump locations has been locked in, goes to the first jump in the buffer and views the buffer in the panel where the jump list was.", 153, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 540 }, +{ PROC_LINKS(goto_jump_at_cursor, 0), false, "goto_jump_at_cursor", 19, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in another view and changes the active panel to the view containing the jump.", 187, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 346 }, +{ PROC_LINKS(goto_jump_at_cursor_same_panel, 0), false, "goto_jump_at_cursor_same_panel", 30, "If the cursor is found to be on a jump location, parses the jump location and brings up the file and position in this view, losing the compilation output or jump list.", 167, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 373 }, +{ PROC_LINKS(goto_line, 0), false, "goto_line", 9, "Queries the user for a number, and jumps the cursor to the corresponding line.", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 748 }, +{ PROC_LINKS(goto_next_jump, 0), false, "goto_next_jump", 14, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, skipping sub jump locations.", 123, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 462 }, +{ PROC_LINKS(goto_next_jump_no_skips, 0), false, "goto_next_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the next jump in the buffer, and does not skip sub jump locations.", 132, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 492 }, +{ PROC_LINKS(goto_prev_jump, 0), false, "goto_prev_jump", 14, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, skipping sub jump locations.", 127, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 479 }, +{ PROC_LINKS(goto_prev_jump_no_skips, 0), false, "goto_prev_jump_no_skips", 23, "If a buffer containing jump locations has been locked in, goes to the previous jump in the buffer, and does not skip sub jump locations.", 136, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 509 }, +{ PROC_LINKS(hide_filebar, 0), false, "hide_filebar", 12, "Sets the current view to hide it's filebar.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 651 }, +{ PROC_LINKS(hide_scrollbar, 0), false, "hide_scrollbar", 14, "Sets the current view to hide it's scrollbar.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 637 }, +{ PROC_LINKS(hms_demo_tutorial, 0), false, "hms_demo_tutorial", 17, "Tutorial for built in 4coder bindings and features.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 869 }, +{ PROC_LINKS(if0_off, 0), false, "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 70 }, +{ PROC_LINKS(if_read_only_goto_position, 0), false, "if_read_only_goto_position", 26, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 562 }, +{ PROC_LINKS(if_read_only_goto_position_same_panel, 0), false, "if_read_only_goto_position_same_panel", 37, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor_same_panel.", 117, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_sticky.cpp", 56, 579 }, +{ PROC_LINKS(increase_face_size, 0), false, "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 673 }, +{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 515 }, +{ PROC_LINKS(interactive_new, 0), true, "interactive_new", 15, "Interactively creates a new file.", 33, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 597 }, +{ PROC_LINKS(interactive_open, 0), true, "interactive_open", 16, "Interactively opens a file.", 27, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 634 }, +{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 563 }, +{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 505 }, +{ PROC_LINKS(jump_to_definition, 0), true, "jump_to_definition", 18, "List all definitions in the code index and jump to one chosen by the user.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_code_index_listers.cpp", 63, 12 }, +{ PROC_LINKS(keyboard_macro_finish_recording, 0), false, "keyboard_macro_finish_recording", 31, "Stop macro recording, do nothing if macro recording is not already started", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 57 }, +{ PROC_LINKS(keyboard_macro_replay, 0), false, "keyboard_macro_replay", 21, "Replay the most recently recorded keyboard macro", 48, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 80 }, +{ PROC_LINKS(keyboard_macro_start_recording, 0), false, "keyboard_macro_start_recording", 30, "Start macro recording, do nothing if macro recording is already started", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_keyboard_macro.cpp", 59, 44 }, +{ PROC_LINKS(kill_buffer, 0), false, "kill_buffer", 11, "Kills the current buffer.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1542 }, +{ PROC_LINKS(kill_tutorial, 0), false, "kill_tutorial", 13, "If there is an active tutorial, kill it.", 40, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 9 }, +{ PROC_LINKS(left_adjust_view, 0), false, "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 211 }, +{ PROC_LINKS(list_all_functions_all_buffers, 0), false, "list_all_functions_all_buffers", 30, "Creates a jump list of lines from all buffers that appear to define or declare functions.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 295 }, +{ PROC_LINKS(list_all_functions_all_buffers_lister, 0), false, "list_all_functions_all_buffers_lister", 37, "Creates a lister of locations that look like function definitions and declarations all buffers.", 95, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 301 }, +{ PROC_LINKS(list_all_functions_current_buffer, 0), false, "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 267 }, +{ PROC_LINKS(list_all_functions_current_buffer_lister, 0), false, "list_all_functions_current_buffer_lister", 40, "Creates a lister of locations that look like function definitions and declarations in the buffer.", 97, "/Users/yuvaldolev/4ed/code/custom/4coder_function_list.cpp", 58, 277 }, +{ PROC_LINKS(list_all_locations, 0), false, "list_all_locations", 18, "Queries the user for a string and lists all exact case-sensitive matches found in all open buffers.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 162 }, +{ PROC_LINKS(list_all_locations_case_insensitive, 0), false, "list_all_locations_case_insensitive", 35, "Queries the user for a string and lists all exact case-insensitive matches found in all open buffers.", 101, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 174 }, +{ PROC_LINKS(list_all_locations_of_identifier, 0), false, "list_all_locations_of_identifier", 32, "Reads a token or word under the cursor and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 186 }, +{ PROC_LINKS(list_all_locations_of_identifier_case_insensitive, 0), false, "list_all_locations_of_identifier_case_insensitive", 49, "Reads a token or word under the cursor and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 192 }, +{ PROC_LINKS(list_all_locations_of_selection, 0), false, "list_all_locations_of_selection", 31, "Reads the string in the selected range and lists all exact case-sensitive mathces in all open buffers.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 198 }, +{ PROC_LINKS(list_all_locations_of_selection_case_insensitive, 0), false, "list_all_locations_of_selection_case_insensitive", 48, "Reads the string in the selected range and lists all exact case-insensitive mathces in all open buffers.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 204 }, +{ PROC_LINKS(list_all_locations_of_type_definition, 0), false, "list_all_locations_of_type_definition", 37, "Queries user for string, lists all locations of strings that appear to define a type whose name matches the input string.", 121, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 210 }, +{ PROC_LINKS(list_all_locations_of_type_definition_of_identifier, 0), false, "list_all_locations_of_type_definition_of_identifier", 51, "Reads a token or word under the cursor and lists all locations of strings that appear to define a type whose name matches it.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 218 }, +{ PROC_LINKS(list_all_substring_locations, 0), false, "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 168 }, +{ PROC_LINKS(list_all_substring_locations_case_insensitive, 0), false, "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 180 }, +{ PROC_LINKS(load_project, 0), false, "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 862 }, +{ PROC_LINKS(load_themes_default_folder, 0), false, "load_themes_default_folder", 26, "Loads all the theme files in the default theme folder.", 54, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 457 }, +{ PROC_LINKS(load_themes_hot_directory, 0), false, "load_themes_hot_directory", 25, "Loads all the theme files in the current hot directory.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 469 }, +{ PROC_LINKS(make_directory_query, 0), false, "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1336 }, +{ PROC_LINKS(miblo_decrement_basic, 0), false, "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 44 }, +{ PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 237 }, +{ PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 249 }, +{ PROC_LINKS(miblo_increment_basic, 0), false, "miblo_increment_basic", 21, "Increment an integer under the cursor by one.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 29 }, +{ PROC_LINKS(miblo_increment_time_stamp, 0), false, "miblo_increment_time_stamp", 26, "Increment a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 231 }, +{ PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "/Users/yuvaldolev/4ed/code/custom/4coder_miblo_numbers.cpp", 58, 243 }, +{ PROC_LINKS(mouse_wheel_change_face_size, 0), false, "mouse_wheel_change_face_size", 28, "Reads the state of the mouse wheel and uses it to either increase or decrease the face size.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 695 }, +{ PROC_LINKS(mouse_wheel_scroll, 0), false, "mouse_wheel_scroll", 18, "Reads the scroll wheel value from the mouse state and scrolls accordingly.", 74, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 265 }, +{ PROC_LINKS(move_down, 0), false, "move_down", 9, "Moves the cursor down one line.", 31, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 338 }, +{ PROC_LINKS(move_down_10, 0), false, "move_down_10", 12, "Moves the cursor down ten lines.", 32, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 350 }, +{ PROC_LINKS(move_down_textual, 0), false, "move_down_textual", 17, "Moves down to the next line of actual text, regardless of line wrapping.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 356 }, +{ PROC_LINKS(move_down_to_blank_line, 0), false, "move_down_to_blank_line", 23, "Seeks the cursor down to the next blank line.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 409 }, +{ PROC_LINKS(move_down_to_blank_line_end, 0), false, "move_down_to_blank_line_end", 27, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 433 }, +{ PROC_LINKS(move_down_to_blank_line_skip_whitespace, 0), false, "move_down_to_blank_line_skip_whitespace", 39, "Seeks the cursor down to the next blank line and places it at the end of the line.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 421 }, +{ PROC_LINKS(move_left, 0), false, "move_left", 9, "Moves the cursor one character to the left.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 439 }, +{ PROC_LINKS(move_left_alpha_numeric_boundary, 0), false, "move_left_alpha_numeric_boundary", 32, "Seek left for boundary between alphanumeric characters and non-alphanumeric characters.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 516 }, +{ PROC_LINKS(move_left_alpha_numeric_or_camel_boundary, 0), false, "move_left_alpha_numeric_or_camel_boundary", 41, "Seek left for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 106, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 530 }, +{ PROC_LINKS(move_left_token_boundary, 0), false, "move_left_token_boundary", 24, "Seek left for the next beginning of a token.", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 488 }, +{ PROC_LINKS(move_left_whitespace_boundary, 0), false, "move_left_whitespace_boundary", 29, "Seek left for the next boundary between whitespace and non-whitespace.", 70, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 473 }, +{ PROC_LINKS(move_left_whitespace_or_token_boundary, 0), false, "move_left_whitespace_or_token_boundary", 38, "Seek left for the next end of a token or boundary between whitespace and non-whitespace.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 502 }, +{ PROC_LINKS(move_line_down, 0), false, "move_line_down", 14, "Swaps the line under the cursor with the line below it, and moves the cursor down with it.", 90, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1376 }, +{ PROC_LINKS(move_line_up, 0), false, "move_line_up", 12, "Swaps the line under the cursor with the line above it, and moves the cursor up with it.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1370 }, +{ PROC_LINKS(move_right, 0), false, "move_right", 10, "Moves the cursor one character to the right.", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 447 }, +{ PROC_LINKS(move_right_alpha_numeric_boundary, 0), false, "move_right_alpha_numeric_boundary", 33, "Seek right for boundary between alphanumeric characters and non-alphanumeric characters.", 88, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 509 }, +{ PROC_LINKS(move_right_alpha_numeric_or_camel_boundary, 0), false, "move_right_alpha_numeric_or_camel_boundary", 42, "Seek right for boundary between alphanumeric characters or camel case word and non-alphanumeric characters.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 523 }, +{ PROC_LINKS(move_right_token_boundary, 0), false, "move_right_token_boundary", 25, "Seek right for the next end of a token.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 481 }, +{ PROC_LINKS(move_right_whitespace_boundary, 0), false, "move_right_whitespace_boundary", 30, "Seek right for the next boundary between whitespace and non-whitespace.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 465 }, +{ PROC_LINKS(move_right_whitespace_or_token_boundary, 0), false, "move_right_whitespace_or_token_boundary", 39, "Seek right for the next end of a token or boundary between whitespace and non-whitespace.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 495 }, +{ PROC_LINKS(move_up, 0), false, "move_up", 7, "Moves the cursor up one line.", 29, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 332 }, +{ PROC_LINKS(move_up_10, 0), false, "move_up_10", 10, "Moves the cursor up ten lines.", 30, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 344 }, +{ PROC_LINKS(move_up_to_blank_line, 0), false, "move_up_to_blank_line", 21, "Seeks the cursor up to the next blank line.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 403 }, +{ PROC_LINKS(move_up_to_blank_line_end, 0), false, "move_up_to_blank_line_end", 25, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 427 }, +{ PROC_LINKS(move_up_to_blank_line_skip_whitespace, 0), false, "move_up_to_blank_line_skip_whitespace", 37, "Seeks the cursor up to the next blank line and places it at the end of the line.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 415 }, +{ PROC_LINKS(open_all_code, 0), false, "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 848 }, +{ PROC_LINKS(open_all_code_recursive, 0), false, "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 854 }, +{ PROC_LINKS(open_file_in_quotes, 0), false, "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1461 }, +{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1792 }, +{ PROC_LINKS(open_long_braces, 0), false, "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 46 }, +{ PROC_LINKS(open_long_braces_break, 0), false, "open_long_braces_break", 22, "At the cursor, insert a '{' and '}break;' separated by a blank line.", 68, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 62 }, +{ PROC_LINKS(open_long_braces_semicolon, 0), false, "open_long_braces_semicolon", 26, "At the cursor, insert a '{' and '};' separated by a blank line.", 63, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 54 }, +{ PROC_LINKS(open_matching_file_cpp, 0), false, "open_matching_file_cpp", 22, "If the current file is a *.cpp or *.h, attempts to open the corresponding *.h or *.cpp file in the other view.", 110, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1493 }, +{ PROC_LINKS(open_panel_hsplit, 0), false, "open_panel_hsplit", 17, "Create a new panel by horizontally splitting the active panel.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 310 }, +{ PROC_LINKS(open_panel_vsplit, 0), false, "open_panel_vsplit", 17, "Create a new panel by vertically splitting the active panel.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 300 }, +{ PROC_LINKS(page_down, 0), false, "page_down", 9, "Scrolls the view down one view height and moves the cursor down one view height.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 374 }, +{ PROC_LINKS(page_up, 0), false, "page_up", 7, "Scrolls the view up one view height and moves the cursor up one view height.", 76, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 366 }, +{ PROC_LINKS(paste, 0), false, "paste", 5, "At the cursor, insert the text at the top of the clipboard.", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 39 }, +{ PROC_LINKS(paste_and_indent, 0), false, "paste_and_indent", 16, "Paste from the top of clipboard and run auto-indent on the newly pasted text.", 77, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 110 }, +{ PROC_LINKS(paste_next, 0), false, "paste_next", 10, "If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.", 156, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 71 }, +{ PROC_LINKS(paste_next_and_indent, 0), false, "paste_next_and_indent", 21, "Paste the next item on the clipboard and run auto-indent on the newly pasted text.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_clipboard.cpp", 54, 117 }, +{ PROC_LINKS(place_in_scope, 0), false, "place_in_scope", 14, "Wraps the code contained in the range between cursor and mark with a new curly brace scope.", 91, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 106 }, +{ PROC_LINKS(profile_clear, 0), false, "profile_clear", 13, "Clear all profiling information from 4coder's self profiler.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 226 }, +{ PROC_LINKS(profile_disable, 0), false, "profile_disable", 15, "Prevent 4coder's self profiler from gathering new profiling information.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 219 }, +{ PROC_LINKS(profile_enable, 0), false, "profile_enable", 14, "Allow 4coder's self profiler to gather new profiling information.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_profile.cpp", 52, 212 }, +{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_profile_inspect.cpp", 60, 886 }, +{ PROC_LINKS(project_command_lister, 0), false, "project_command_lister", 22, "Open a lister of all commands in the currently loaded project.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1289 }, +{ PROC_LINKS(project_fkey_command, 0), false, "project_fkey_command", 20, "Run an 'fkey command' configured in a project.4coder file. Determines the index of the 'fkey command' by which function key or numeric key was pressed to trigger the command.", 175, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 870 }, +{ PROC_LINKS(project_go_to_root_directory, 0), false, "project_go_to_root_directory", 28, "Changes 4coder's hot directory to the root directory of the currently loaded project. With no loaded project nothing hapepns.", 125, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 896 }, +{ PROC_LINKS(query_replace, 0), false, "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1149 }, +{ PROC_LINKS(query_replace_identifier, 0), false, "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1170 }, +{ PROC_LINKS(query_replace_selection, 0), false, "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1186 }, +{ PROC_LINKS(redo, 0), false, "redo", 4, "Advances forwards through the undo history of the current buffer.", 65, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1631 }, +{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1716 }, +{ PROC_LINKS(rename_file_query, 0), false, "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1298 }, +{ PROC_LINKS(reopen, 0), false, "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1560 }, +{ PROC_LINKS(replace_in_all_buffers, 0), false, "replace_in_all_buffers", 22, "Queries the user for a needle and string. Replaces all occurences of needle with string in all editable buffers.", 112, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1059 }, +{ PROC_LINKS(replace_in_buffer, 0), false, "replace_in_buffer", 17, "Queries the user for a needle and string. Replaces all occurences of needle with string in the active buffer.", 109, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1050 }, +{ PROC_LINKS(replace_in_range, 0), false, "replace_in_range", 16, "Queries the user for a needle and string. Replaces all occurences of needle with string in the range between cursor and the mark in the active buffer.", 150, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1041 }, +{ PROC_LINKS(reverse_search, 0), false, "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 982 }, +{ PROC_LINKS(reverse_search_identifier, 0), false, "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 994 }, +{ PROC_LINKS(save, 0), false, "save", 4, "Saves the current buffer.", 25, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1550 }, +{ PROC_LINKS(save_all_dirty_buffers, 0), false, "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 382 }, +{ PROC_LINKS(save_to_query, 0), false, "save_to_query", 13, "Queries the user for a file name and saves the contents of the current buffer, altering the buffer's name too.", 110, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1265 }, +{ PROC_LINKS(search, 0), false, "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 976 }, +{ PROC_LINKS(search_identifier, 0), false, "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 988 }, +{ PROC_LINKS(seek_beginning_of_line, 0), false, "seek_beginning_of_line", 22, "Seeks the cursor to the beginning of the visual line.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2172 }, +{ PROC_LINKS(seek_beginning_of_textual_line, 0), false, "seek_beginning_of_textual_line", 30, "Seeks the cursor to the beginning of the line across all text.", 62, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2160 }, +{ PROC_LINKS(seek_end_of_line, 0), false, "seek_end_of_line", 16, "Seeks the cursor to the end of the visual line.", 47, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2178 }, +{ PROC_LINKS(seek_end_of_textual_line, 0), false, "seek_end_of_textual_line", 24, "Seeks the cursor to the end of the line across all text.", 56, "/Users/yuvaldolev/4ed/code/custom/4coder_helper.cpp", 51, 2166 }, +{ PROC_LINKS(select_all, 0), false, "select_all", 10, "Puts the cursor at the top of the file, and the mark at the bottom of the file.", 79, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 539 }, +{ PROC_LINKS(select_next_scope_absolute, 0), false, "select_next_scope_absolute", 26, "Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.", 102, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 57 }, +{ PROC_LINKS(select_next_scope_after_current, 0), false, "select_next_scope_after_current", 31, "If a scope is selected, find first scope that starts after the selected scope. Otherwise find the first scope that starts after the cursor.", 139, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 66 }, +{ PROC_LINKS(select_prev_scope_absolute, 0), false, "select_prev_scope_absolute", 26, "Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.", 103, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 82 }, +{ PROC_LINKS(select_prev_top_most_scope, 0), false, "select_prev_top_most_scope", 26, "Finds the first scope that starts before the cursor, then finds the top most scope that contains that scope.", 108, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 99 }, +{ PROC_LINKS(select_surrounding_scope, 0), false, "select_surrounding_scope", 24, "Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.", 107, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 27 }, +{ PROC_LINKS(select_surrounding_scope_maximal, 0), false, "select_surrounding_scope_maximal", 32, "Selects the top-most scope that surrounds the cursor.", 53, "/Users/yuvaldolev/4ed/code/custom/4coder_scope_commands.cpp", 59, 39 }, +{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 125 }, +{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 112 }, +{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 86 }, +{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "/Users/yuvaldolev/4ed/code/custom/4coder_eol.cpp", 48, 99 }, +{ PROC_LINKS(set_mark, 0), false, "set_mark", 8, "Sets the mark to the current position of the cursor.", 52, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 115 }, +{ PROC_LINKS(set_mode_to_notepad_like, 0), false, "set_mode_to_notepad_like", 24, "Sets the edit mode to Notepad like.", 35, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 427 }, +{ PROC_LINKS(set_mode_to_original, 0), false, "set_mode_to_original", 20, "Sets the edit mode to 4coder original.", 38, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 421 }, +{ PROC_LINKS(setup_build_bat, 0), false, "setup_build_bat", 15, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1237 }, +{ PROC_LINKS(setup_build_bat_and_sh, 0), false, "setup_build_bat_and_sh", 22, "Queries the user for several configuration options and initializes a new build batch script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1249 }, +{ PROC_LINKS(setup_build_sh, 0), false, "setup_build_sh", 14, "Queries the user for several configuration options and initializes a new build shell script.", 92, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1243 }, +{ PROC_LINKS(setup_new_project, 0), false, "setup_new_project", 17, "Queries the user for several configuration options and initializes a new 4coder project with build scripts for every OS.", 120, "/Users/yuvaldolev/4ed/code/custom/4coder_project_commands.cpp", 61, 1230 }, +{ PROC_LINKS(show_filebar, 0), false, "show_filebar", 12, "Sets the current view to show it's filebar.", 43, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 644 }, +{ PROC_LINKS(show_scrollbar, 0), false, "show_scrollbar", 14, "Sets the current view to show it's scrollbar.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 630 }, +{ PROC_LINKS(show_the_log_graph, 0), true, "show_the_log_graph", 18, "Parses *log* and displays the 'log graph' UI", 44, "/Users/yuvaldolev/4ed/code/custom/4coder_log_parser.cpp", 55, 994 }, +{ PROC_LINKS(snipe_backward_whitespace_or_token_boundary, 0), false, "snipe_backward_whitespace_or_token_boundary", 43, "Delete a single, whole token on or to the left of the cursor and post it to the clipboard.", 90, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 179 }, +{ PROC_LINKS(snipe_forward_whitespace_or_token_boundary, 0), false, "snipe_forward_whitespace_or_token_boundary", 42, "Delete a single, whole token on or to the right of the cursor and post it to the clipboard.", 91, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 187 }, +{ PROC_LINKS(snippet_lister, 0), true, "snippet_lister", 14, "Opens a snippet lister for inserting whole pre-written snippets of text.", 72, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 237 }, +{ PROC_LINKS(suppress_mouse, 0), false, "suppress_mouse", 14, "Hides the mouse and causes all mosue input (clicks, position, wheel) to be ignored.", 83, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 403 }, +{ PROC_LINKS(swap_panels, 0), false, "swap_panels", 11, "Swaps the active panel with it's sibling.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1518 }, +{ PROC_LINKS(theme_lister, 0), true, "theme_lister", 12, "Opens an interactive list of all registered themes.", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_lists.cpp", 50, 692 }, +{ PROC_LINKS(to_lowercase, 0), false, "to_lowercase", 12, "Converts all ascii text in the range between the cursor and the mark to lowercase.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 565 }, +{ PROC_LINKS(to_uppercase, 0), false, "to_uppercase", 12, "Converts all ascii text in the range between the cursor and the mark to uppercase.", 82, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 552 }, +{ PROC_LINKS(toggle_filebar, 0), false, "toggle_filebar", 14, "Toggles the visibility status of the current view's filebar.", 60, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 658 }, +{ PROC_LINKS(toggle_fps_meter, 0), false, "toggle_fps_meter", 16, "Toggles the visibility of the FPS performance meter", 51, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 667 }, +{ PROC_LINKS(toggle_fullscreen, 0), false, "toggle_fullscreen", 17, "Toggle fullscreen mode on or off. The change(s) do not take effect until the next frame.", 89, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 451 }, +{ PROC_LINKS(toggle_highlight_enclosing_scopes, 0), false, "toggle_highlight_enclosing_scopes", 33, "In code files scopes surrounding the cursor are highlighted with distinguishing colors.", 87, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 439 }, +{ PROC_LINKS(toggle_highlight_line_at_cursor, 0), false, "toggle_highlight_line_at_cursor", 31, "Toggles the line highlight at the cursor.", 41, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 433 }, +{ PROC_LINKS(toggle_line_numbers, 0), false, "toggle_line_numbers", 19, "Toggles the left margin line numbers.", 37, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 721 }, +{ PROC_LINKS(toggle_line_wrap, 0), false, "toggle_line_wrap", 16, "Toggles the line wrap setting on this buffer.", 45, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 727 }, +{ PROC_LINKS(toggle_mouse, 0), false, "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 415 }, +{ PROC_LINKS(toggle_paren_matching_helper, 0), false, "toggle_paren_matching_helper", 28, "In code files matching parentheses pairs are colored with distinguishing colors.", 80, "/Users/yuvaldolev/4ed/code/custom/4coder_default_framework.cpp", 62, 445 }, +{ PROC_LINKS(toggle_show_whitespace, 0), false, "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 712 }, +{ PROC_LINKS(toggle_virtual_whitespace, 0), false, "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_code_index.cpp", 55, 1160 }, +{ PROC_LINKS(tutorial_maximize, 0), false, "tutorial_maximize", 17, "Expand the tutorial window", 26, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 20 }, +{ PROC_LINKS(tutorial_minimize, 0), false, "tutorial_minimize", 17, "Shrink the tutorial window", 26, "/Users/yuvaldolev/4ed/code/custom/4coder_tutorial.cpp", 53, 34 }, +{ PROC_LINKS(uncomment_line, 0), false, "uncomment_line", 14, "If present, delete '//' at the beginning of the line after leading whitespace.", 78, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 137 }, +{ PROC_LINKS(undo, 0), false, "undo", 4, "Advances backwards through the undo history of the current buffer.", 66, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1618 }, +{ PROC_LINKS(undo_all_buffers, 0), false, "undo_all_buffers", 16, "Advances backward through the undo history in the buffer containing the most recent regular edit.", 97, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1645 }, +{ PROC_LINKS(view_buffer_other_panel, 0), false, "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 1506 }, +{ PROC_LINKS(view_jump_list_with_lister, 0), false, "view_jump_list_with_lister", 26, "When executed on a buffer with jumps, creates a persistent lister for all the jumps", 83, "/Users/yuvaldolev/4ed/code/custom/4coder_jump_lister.cpp", 56, 59 }, +{ PROC_LINKS(word_complete, 0), false, "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 392 }, +{ PROC_LINKS(word_complete_drop_down, 0), false, "word_complete_drop_down", 23, "Word complete with drop down menu.", 34, "/Users/yuvaldolev/4ed/code/custom/4coder_search.cpp", 51, 639 }, +{ PROC_LINKS(write_block, 0), false, "write_block", 11, "At the cursor, insert a block comment.", 38, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 94 }, +{ PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 82 }, +{ PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 88 }, +{ PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts an underscore.", 22, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 67 }, +{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "/Users/yuvaldolev/4ed/code/custom/4coder_auto_indent.cpp", 56, 395 }, +{ PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 59 }, +{ PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 76 }, +{ PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "/Users/yuvaldolev/4ed/code/custom/4coder_base_commands.cpp", 58, 73 }, +{ PROC_LINKS(write_zero_struct, 0), false, "write_zero_struct", 17, "At the cursor, insert a ' = {};'.", 33, "/Users/yuvaldolev/4ed/code/custom/4coder_combined_write_commands.cpp", 68, 100 }, }; static i32 fcoder_metacmd_ID_allow_mouse = 0; static i32 fcoder_metacmd_ID_auto_indent_line_at_cursor = 1; diff --git a/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator b/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator index d94c7b9522fc39f45009ef4ef8c1d9cfafae8580..133e453d1c931a6805c0b6e109b54aada975ee7d 100644 GIT binary patch literal 136740 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 aPWq;gb0w``BQS}fj=Gqpe2u_k^nU?nKY9-U literal 138399 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 HK1Tl^sgkr2 diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp deleted file mode 100644 index b2636154..00000000 --- a/platform_mac/mac_4ed.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* Mac C++ layer for 4coder */ - -#include "4coder_base_types.h" -#include "4coder_version.h" -#include "4coder_events.h" - -#include "4coder_table.h" -#include "4coder_types.h" -#include "4coder_default_colors.h" - -#include "4coder_system_types.h" -#define STATIC_LINK_API -#include "generated/system_api.h" - -#include "4ed_font_interface.h" -#define STATIC_LINK_API -#include "generated/graphics_api.h" -#define STATIC_LINK_API -#include "generated/font_api.h" - -#include "4ed_font_set.h" -#include "4ed_render_target.h" -#include "4ed_search_list.h" -#include "4ed.h" - -#include "generated/system_api.cpp" -#include "generated/graphics_api.cpp" -#include "generated/font_api.cpp" - -#include "4coder_base_types.cpp" -#include "4coder_stringf.cpp" -#include "4coder_events.cpp" -#include "4coder_hash_functions.cpp" -#include "4coder_table.cpp" -#include "4coder_log.cpp" - -#include "4ed_search_list.cpp" - -#include "mac_objective_c_to_cpp_links.h" - -#include // NOTE(yuval): Used for opendir, readdir -#include // NOTE(yuval): Used for errno -#include // NOTE(yuval): Used for open -#include // NOTE(yuval): Used for getcwd, read, write -#include // NOTE(yuval): Used for stat -#include // NOTE(yuval): Used for struct stat - -#include // NOTE(yuval): Used for free - -//////////////////////////////// - -#define SLASH '/' -#define DLL "so" - -#include "4coder_hash_functions.cpp" -#include "4coder_system_allocator.cpp" -#include "4coder_malloc_allocator.cpp" -#include "4coder_codepoint_map.cpp" - -#include "4ed_mem.cpp" -#include "4ed_font_set.cpp" - -//////////////////////////////// - -struct Mac_Vars { - Thread_Context *tctx; - - Arena* frame_arena; - - String_Const_u8 binary_path; -}; - -//////////////////////////////// - -Mac_Vars mac_vars; -global Render_Target target; - -//////////////////////////////// - -#include "mac_4ed_functions.cpp" - -//////////////////////////////// - -external String_Const_u8 -mac_SCu8(u8* str, u64 size){ - String_Const_u8 result = SCu8(str, size); - return(result); -} - -external String_Const_u8 -mac_push_string_copy(Arena *arena, String_Const_u8 src){ - String_Const_u8 result = push_string_copy(arena, src); - return(result); -} - -external void -mac_init(){ - Arena test_arena = make_arena_malloc(); - File_List list = system_get_file_list(&test_arena, - string_u8_litexpr("/Users/yuvaldolev/Desktop")); - - for (u32 index = 0; index < list.count; ++index) { - File_Info* info = list.infos[index]; - - printf("File_Info{file_name:'%.*s', " - "attributes:{size:%llu, last_write_time:%llu, flags:{IsDirectory:%d}}}\n", - (i32)info->file_name.size, info->file_name.str, - info->attributes.size, info->attributes.last_write_time, - ((info->attributes.flags & FileAttribute_IsDirectory) != 0)); - } - -#if 0 - // NOTE(yuval): Context Setup - Thread_Context _tctx = {}; - thread_ctx_init(&_tctx, ThreadKind_Main, - get_base_allocator_system(), - get_base_allocator_system()); - - block_zero_struct(&mac_vars); - mac_vars.tctx = &_tctx; - - API_VTable_system system_vtable = {}; - system_api_fill_vtable(&system_vtable); - - API_VTable_graphics graphics_vtable = {}; - graphics_api_fill_vtable(&graphics_vtable); - - API_VTable_font font_vtable = {}; - font_api_fill_vtable(&font_vtable); - - // NOTE(yuval): Memory - mac_vars.frame_arena = reserve_arena(mac_vars.tctx); - target.arena = make_arena_system(KB(256)); - - mac_vars.cursor_show = MouseCursorShow_Always; - mac_vars.prev_cursor_show = MouseCursorShow_Always; - - dll_init_sentinel(&mac_vars.free_mac_objects); - dll_init_sentinel(&mac_vars.timer_objects); -#endif -} \ No newline at end of file diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index c2519d04..de73ec52 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,17 +1,47 @@ /* Mac Objective C layer for 4coder */ #include "4coder_base_types.h" - -#if 1 -#include "4coder_table.h" +#include "4coder_version.h" #include "4coder_events.h" + +#include "4coder_table.h" + // NOTE(allen): This is a very unfortunate hack, but hopefully there will never be a need to use the Marker // type in the platform layer. If that changes then instead change the name of Marker and make a transition // macro that is only included in custom code. #define Marker Marker__SAVE_THIS_IDENTIFIER #include "4coder_types.h" #undef Marker -#endif + +#include "4coder_default_colors.h" + +#include "4coder_system_types.h" +#define STATIC_LINK_API +#include "generated/system_api.h" + +#include "4ed_font_interface.h" +#define STATIC_LINK_API +#include "generated/graphics_api.h" +#define STATIC_LINK_API +#include "generated/font_api.h" + +#include "4ed_font_set.h" +#include "4ed_render_target.h" +#include "4ed_search_list.h" +#include "4ed.h" + +#include "generated/system_api.cpp" +#include "generated/graphics_api.cpp" +#include "generated/font_api.cpp" + +#include "4coder_base_types.cpp" +#include "4coder_stringf.cpp" +#include "4coder_events.cpp" +#include "4coder_hash_functions.cpp" +#include "4coder_table.cpp" +#include "4coder_log.cpp" + +#include "4ed_search_list.cpp" #include "mac_objective_c_to_cpp_links.h" @@ -19,13 +49,22 @@ #undef internal #undef global #undef external -#include +#import #include // NOTE(yuval): Used for proc_pidpath -#include // NOTE(yuval): Used for pid_t -#include // NOTE(yuval): Used for getpid +#include // NOTE(yuval): Used for opendir, readdir +#include // NOTE(yuval): Used for errno +#include // NOTE(yuval): Used for open +#include // NOTE(yuval): Used for getcwd, read, write, getpid +#include // NOTE(yuval): Used for stat +#include // NOTE(yuval): Used for struct stat, pid_t +#include // NOTE(yuval): Used for free + +#define function static +#define internal static +#define global static #define external extern "C" @interface App_Delegate : NSObject @@ -52,49 +91,39 @@ } @end -#if 0 -external File_List -mac_get_file_list(Arena* arena, String_Const_u8 directory){ - File_List result = {}; - - NSString *directory_ns_string = - [[NSString alloc] - initWithBytes:directory.data length:directory.size encoding:NSUTF8StringEncoding]; - - NSFileManager *file_manager = [NSFileManager defaultManager]; - NSDirectoryEnumerator *dirEnum = [file_manager enumeratorAtPath:directory_ns_string]; - - NSString *filename; - while ((filename = [dirEnum nextObject])){ - NSLog(@"%@", filename); - } - - [directory_ns_string release]; -} -#endif +//////////////////////////////// -external String_Const_u8 -mac_standardize_path(Arena* arena, String_Const_u8 path){ - NSString *path_ns_str = - [[NSString alloc] initWithBytes:path.data length:path.size encoding:NSUTF8StringEncoding]; - - NSString *standardized_path_ns_str = [path_ns_str stringByStandardizingPath]; - String_Const_u8 standardized_path = mac_SCu8((u8*)[standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); - - String_Const_u8 result = mac_push_string_copy(arena, standardized_path); - - [path_ns_str release]; - - return(result); -} +#define SLASH '/' +#define DLL "so" -external i32 -mac_get_binary_path(void *buffer, u32 size){ - pid_t pid = getpid(); - i32 bytes_read = proc_pidpath(pid, buffer, size); +#include "4coder_hash_functions.cpp" +#include "4coder_system_allocator.cpp" +#include "4coder_malloc_allocator.cpp" +#include "4coder_codepoint_map.cpp" + +#include "4ed_mem.cpp" +#include "4ed_font_set.cpp" + +//////////////////////////////// + +struct Mac_Vars { + Thread_Context *tctx; - return(bytes_read); -} + Arena* frame_arena; + + String_Const_u8 binary_path; +}; + +//////////////////////////////// + +Mac_Vars mac_vars; +global Render_Target target; + +//////////////////////////////// + +#import "mac_4ed_functions.mm" + +//////////////////////////////// int main(int arg_count, char **args){ @@ -108,7 +137,49 @@ main(int arg_count, char **args){ [NSApp finishLaunching]; - mac_init(); + Arena test_arena = make_arena_malloc(); + File_List list = system_get_file_list(&test_arena, + string_u8_litexpr("/Users/yuvaldolev/Desktop/imgui")); + + for (u32 index = 0; index < list.count; ++index) { + File_Info* info = list.infos[index]; + + printf("File_Info{file_name:'%.*s', " + "attributes:{size:%llu, last_write_time:%llu, flags:{IsDirectory:%d}}}\n", + (i32)info->file_name.size, info->file_name.str, + info->attributes.size, info->attributes.last_write_time, + ((info->attributes.flags & FileAttribute_IsDirectory) != 0)); + } + +#if 0 + // NOTE(yuval): Context Setup + Thread_Context _tctx = {}; + thread_ctx_init(&_tctx, ThreadKind_Main, + get_base_allocator_system(), + get_base_allocator_system()); + + block_zero_struct(&mac_vars); + mac_vars.tctx = &_tctx; + + API_VTable_system system_vtable = {}; + system_api_fill_vtable(&system_vtable); + + API_VTable_graphics graphics_vtable = {}; + graphics_api_fill_vtable(&graphics_vtable); + + API_VTable_font font_vtable = {}; + font_api_fill_vtable(&font_vtable); + + // NOTE(yuval): Memory + mac_vars.frame_arena = reserve_arena(mac_vars.tctx); + target.arena = make_arena_system(KB(256)); + + mac_vars.cursor_show = MouseCursorShow_Always; + mac_vars.prev_cursor_show = MouseCursorShow_Always; + + dll_init_sentinel(&mac_vars.free_mac_objects); + dll_init_sentinel(&mac_vars.timer_objects); +#endif #if 0 // NOTE(yuval): Application Core Update diff --git a/platform_mac/mac_4ed_functions.cpp b/platform_mac/mac_4ed_functions.mm similarity index 94% rename from platform_mac/mac_4ed_functions.cpp rename to platform_mac/mac_4ed_functions.mm index 2c22135e..00b4ecc4 100644 --- a/platform_mac/mac_4ed_functions.cpp +++ b/platform_mac/mac_4ed_functions.mm @@ -31,7 +31,9 @@ system_get_path_sig(){ if (!has_stashed_4ed_path){ local_const i32 binary_path_capacity = KB(32); u8 *memory = (u8*)system_memory_allocate(binary_path_capacity, string_u8_litexpr(file_name_line_number)); - i32 size = mac_get_binary_path(memory, binary_path_capacity); + + pid_t pid = getpid(); + i32 size = proc_pidpath(pid, memory, binary_path_capacity); Assert(size <= binary_path_capacity - 1); mac_vars.binary_path = SCu8(memory, size); @@ -50,7 +52,16 @@ system_get_path_sig(){ function system_get_canonical_sig(){ - String_Const_u8 result = mac_standardize_path(arena, name); + NSString *path_ns_str = + [[NSString alloc] initWithBytes:name.data length:name.size encoding:NSUTF8StringEncoding]; + + NSString *standardized_path_ns_str = [path_ns_str stringByStandardizingPath]; + String_Const_u8 standardized_path = mac_SCu8((u8*)[standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + + String_Const_u8 result = push_string_copy(arena, standardized_path); + + [path_ns_str release]; + return(result); } From a91158ebf0dcc2c1a5114d63d48c275dbf707681 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sat, 28 Dec 2019 01:54:33 +0200 Subject: [PATCH 23/67] Implemented all system library handling functions. --- platform_mac/mac_4ed.mm | 9 +++-- platform_mac/mac_4ed_functions.mm | 63 +++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index de73ec52..b1bd4d7f 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,4 +1,4 @@ -/* Mac Objective C layer for 4coder */ +/* Mac Objective-C layer for 4coder */ #include "4coder_base_types.h" #include "4coder_version.h" @@ -54,6 +54,7 @@ #include // NOTE(yuval): Used for proc_pidpath #include // NOTE(yuval): Used for opendir, readdir +#include // NOTE(yuval): Used for dlopen, dlclose, dlsym #include // NOTE(yuval): Used for errno #include // NOTE(yuval): Used for open #include // NOTE(yuval): Used for getcwd, read, write, getpid @@ -67,6 +68,8 @@ #define global static #define external extern "C" +//////////////////////////////// + @interface App_Delegate : NSObject @end @@ -116,7 +119,7 @@ struct Mac_Vars { //////////////////////////////// -Mac_Vars mac_vars; +global Mac_Vars mac_vars; global Render_Target target; //////////////////////////////// @@ -139,7 +142,7 @@ main(int arg_count, char **args){ Arena test_arena = make_arena_malloc(); File_List list = system_get_file_list(&test_arena, - string_u8_litexpr("/Users/yuvaldolev/Desktop/imgui")); + string_u8_litexpr("/Users/yuvaldolev/Desktop")); for (u32 index = 0; index < list.count; ++index) { File_Info* info = list.infos[index]; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 00b4ecc4..291e3061 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -200,13 +200,25 @@ system_quick_file_attributes_sig(){ return(result); } +function inline Plat_Handle +mac_to_plat_handle(i32 fd){ + Plat_Handle result = *(Plat_Handle*)(&fd); + return result; +} + +function inline i32 +mac_to_fd(Plat_Handle handle){ + i32 result = *(i32*)(&handle); + return result; +} + function system_load_handle_sig(){ b32 result = false; i32 fd = open(file_name, O_RDONLY); if ((fd != -1) && (fd != 0)) { - *(i32*)out = fd; + *out = mac_to_plat_handle(fd); result = true; } @@ -215,7 +227,7 @@ system_load_handle_sig(){ function system_load_attributes_sig(){ - i32 fd = *(i32*)(&handle); + i32 fd = mac_to_fd(handle); File_Attributes result = mac_file_attributes_from_fd(fd); return(result); @@ -223,7 +235,7 @@ system_load_attributes_sig(){ function system_load_file_sig(){ - i32 fd = *(i32*)(&handle); + i32 fd = mac_to_fd(handle); do{ ssize_t bytes_read = read(fd, buffer, size); @@ -246,7 +258,7 @@ function system_load_close_sig(){ b32 result = true; - i32 fd = *(i32*)(&handle); + i32 fd = mac_to_fd(handle); if (close(fd) == -1){ // NOTE(yuval): An error occured while close the file descriptor result = false; @@ -289,29 +301,58 @@ system_save_file_sig(){ //////////////////////////////// +function inline System_Library +mac_to_system_library(void* dl_handle){ + System_Library result = *(System_Library*)(&dl_handle); + return result; +} + +function inline void* +mac_to_dl_handle(System_Library system_lib){ + void* result = *(void**)(&system_lib); + return result; +} + function system_load_library_sig(){ b32 result = false; - NotImplemented; + void* lib = 0; + + // NOTE(yuval): Open library handle + { + Temp_Memory temp = begin_temp(scratch); + + char* c_file_name = push_array(scratch, char, file_name.size + 1); + block_copy(c_file_name, file_name.str, file_name.size); + c_file_name[file_name.size] = 0; + + lib = dlopen(c_file_name, RTLD_LAZY | RTLD_GLOBAL); + + end_temp(temp); + } + + if (lib){ + *out = mac_to_system_library(lib); + result = true; + } return(result); } function system_release_library_sig(){ - b32 result = false; - - NotImplemented; + void* lib = mac_to_dl_handle(handle); + i32 rc = dlclose(lib); + b32 result = (rc == 0); return(result); } function system_get_proc_sig(){ - Void_Func* result = 0; - - NotImplemented; + void* lib = mac_to_dl_handle(handle); + Void_Func* result = (Void_Func*)dlsym(lib, proc_name); return(result); } From 8020dcf385dc65ab8f69b4d26334acb65a985da8 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 29 Dec 2019 00:11:34 +0200 Subject: [PATCH 24/67] Implemented system_now_time. --- platform_mac/mac_4ed_functions.mm | 12 +++++++++--- platform_mac/mac_4ed_old.m | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 291e3061..a59fbad1 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -56,7 +56,7 @@ system_get_canonical_sig(){ [[NSString alloc] initWithBytes:name.data length:name.size encoding:NSUTF8StringEncoding]; NSString *standardized_path_ns_str = [path_ns_str stringByStandardizingPath]; - String_Const_u8 standardized_path = mac_SCu8((u8*)[standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + String_Const_u8 standardized_path = SCu8((u8*)[standardized_path_ns_str UTF8String],[standardized_path_ns_str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); String_Const_u8 result = push_string_copy(arena, standardized_path); @@ -361,9 +361,15 @@ system_get_proc_sig(){ function system_now_time_sig(){ - u64 result = 0; + u64 now = mach_absolute_time(); - NotImplemented; + // NOTE(yuval): Elapsed nanoseconds calculation + u64 result = (u64)(((f32)now) * + ((f32)mac_vars.timebase_info.numer) / + ((f32)mac_vars.timebase_info.denom)); + + // NOTE(yuval): Conversion to useconds + result *= 1.0E-3; return(result); } diff --git a/platform_mac/mac_4ed_old.m b/platform_mac/mac_4ed_old.m index ebadc1ca..fad0565e 100644 --- a/platform_mac/mac_4ed_old.m +++ b/platform_mac/mac_4ed_old.m @@ -814,9 +814,9 @@ osx_list_loadable_fonts(void){ NSString *font_n = fonts[i]; char *font_n_c = (char*)[font_n UTF8String]; NSFont *font = [font_manager - fontWithFamily:font_n - traits:NSUnboldFontMask|NSUnitalicFontMask - weight:5 + fontWithFamily:font_n + traits:NSUnboldFontMask|NSUnitalicFontMask + weight:5 size:12]; NSString *path = get_font_path(font); char *path_c = 0; From 76069e9ac1e61a53e6ec19444716ad4de47e5c75 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 29 Dec 2019 03:09:28 +0200 Subject: [PATCH 25/67] Created basic Mac_Object structure and mac object allocation function. --- platform_mac/mac_4ed.mm | 139 +++++++++++++++++++++++------- platform_mac/mac_4ed_functions.mm | 24 +++--- 2 files changed, 120 insertions(+), 43 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index b1bd4d7f..501614b1 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -70,6 +70,113 @@ //////////////////////////////// +#define SLASH '/' +#define DLL "so" + +#include "4coder_hash_functions.cpp" +#include "4coder_system_allocator.cpp" +#include "4coder_malloc_allocator.cpp" +#include "4coder_codepoint_map.cpp" + +#include "4ed_mem.cpp" +#include "4ed_font_set.cpp" + +//////////////////////////////// + +typedef i32 Win32_Object_Kind; +enum{ + Win32ObjectKind_ERROR = 0, + Win32ObjectKind_Timer = 1, + Win32ObjectKind_Thread = 2, + Win32ObjectKind_Mutex = 3, + Win32ObjectKind_CV = 4, +}; + +struct Mac_Object{ + Node node; + Mac_Object_Kind kind; + + union{ + struct{ + NSTimer* timer; + } timer; + }; +}; + +struct Mac_Vars { + Thread_Context *tctx; + + Arena* frame_arena; + + String_Const_u8 binary_path; + + Node free_mac_objects; + Node timer_objects; +}; + +//////////////////////////////// + +global Mac_Vars mac_vars; +global Render_Target target; + +//////////////////////////////// + +function inline Plat_Handle +mac_to_plat_handle(Mac_Object* object){ + Plat_Handle result = *(Plat_Handle*)(&object); + return(result); +} + +function inline Mac_Object* +mac_to_object(Plat_Handle handle){ + Mac_Object* result = *(Mac_Object**)(&handle); + return(result); +} + +function +mac_alloc_object(Mac_Object_Kind kind){ + Mac_Object* result = 0; + + if (mac_vars.free_mac_objects.next != &mac_vars.free_mac_objects){ + result = CastFromMember(Mac_Object, node, mac_vars.free_mac_objects.next); + } + + if (!result){ + i32 count = 512; + Mac_Object* objects = (Mac_Object*)system_memory_allocate(count * sizeof(Mac_Object), file_name_line_number); + + // NOTE(yuval): Link the first chain of the dll to the sentinel + objects[0].node.prev = &mac_vars.free_mac_objects; + mac_vars.free_mac_objects.next = &objects[0].node; + + // NOTE(yuval): Link all dll chains to each other + for (i32 chain_index = 1; chain_index < count; chain_index += 1){ + objects[chain_index - 1].node.next = &objects[chain_index].node; + objects[chain_index].node.prev = &objects[chain_index - 1].node; + } + + // NOTE(yuval): Link the last chain of the dll to the sentinel + objects[count - 1].node.next = &mac_vars.free_mac_objects; + mac_vars.free_mac_objects.prev = &objects[count - 1].node; + + result = CastFromMember(Mac_Object, node, mac_vars.free_mac_objects.next); + } + + Assert(result); + dll_remove(&result->node); + block_zero_struct(result); + result->kind = kind; + + return(result); +} + + +//////////////////////////////// + +#import "mac_4ed_functions.mm" + +//////////////////////////////// + @interface App_Delegate : NSObject @end @@ -96,38 +203,6 @@ //////////////////////////////// -#define SLASH '/' -#define DLL "so" - -#include "4coder_hash_functions.cpp" -#include "4coder_system_allocator.cpp" -#include "4coder_malloc_allocator.cpp" -#include "4coder_codepoint_map.cpp" - -#include "4ed_mem.cpp" -#include "4ed_font_set.cpp" - -//////////////////////////////// - -struct Mac_Vars { - Thread_Context *tctx; - - Arena* frame_arena; - - String_Const_u8 binary_path; -}; - -//////////////////////////////// - -global Mac_Vars mac_vars; -global Render_Target target; - -//////////////////////////////// - -#import "mac_4ed_functions.mm" - -//////////////////////////////// - int main(int arg_count, char **args){ @autoreleasepool{ diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index a59fbad1..a4009db7 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -203,13 +203,13 @@ system_quick_file_attributes_sig(){ function inline Plat_Handle mac_to_plat_handle(i32 fd){ Plat_Handle result = *(Plat_Handle*)(&fd); - return result; + return(result); } function inline i32 mac_to_fd(Plat_Handle handle){ i32 result = *(i32*)(&handle); - return result; + return(result); } function @@ -304,13 +304,13 @@ system_save_file_sig(){ function inline System_Library mac_to_system_library(void* dl_handle){ System_Library result = *(System_Library*)(&dl_handle); - return result; + return(result); } function inline void* mac_to_dl_handle(System_Library system_lib){ void* result = *(void**)(&system_lib); - return result; + return(result); } function @@ -363,14 +363,13 @@ function system_now_time_sig(){ u64 now = mach_absolute_time(); - // NOTE(yuval): Elapsed nanoseconds calculation - u64 result = (u64)(((f32)now) * - ((f32)mac_vars.timebase_info.numer) / - ((f32)mac_vars.timebase_info.denom)); + // NOTE(yuval): Now time nanoseconds conversion + f64 now_nano = (f64)(((f64)now) * + ((f64)mac_vars.timebase_info.numer) / + ((f64)mac_vars.timebase_info.denom)); // NOTE(yuval): Conversion to useconds - result *= 1.0E-3; - + u64 result = (u64)(now_nano * 1.0E-3); return(result); } @@ -378,7 +377,10 @@ function system_wake_up_timer_create_sig(){ Plat_Handle result = {}; - NotImplemented; + NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.0 + target: view + selector: @selector(requestDisplay) + userInfo: nil repeats:NO]; return(result); } From dc213307a92c8f3e477b7f9f2080a6940a7b2ed9 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 30 Dec 2019 00:34:53 +0200 Subject: [PATCH 26/67] Implemented all system wake up timer functions. --- platform_mac/mac_4ed.mm | 38 +++++++++++-------- platform_mac/mac_4ed_functions.mm | 63 ++++++++++++++++++------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 501614b1..9a87b069 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -83,13 +83,13 @@ //////////////////////////////// -typedef i32 Win32_Object_Kind; +typedef i32 Mac_Object_Kind; enum{ - Win32ObjectKind_ERROR = 0, - Win32ObjectKind_Timer = 1, - Win32ObjectKind_Thread = 2, - Win32ObjectKind_Mutex = 3, - Win32ObjectKind_CV = 4, + MacObjectKind_ERROR = 0, + MacObjectKind_Timer = 1, + MacObjectKind_Thread = 2, + MacObjectKind_Mutex = 3, + MacObjectKind_CV = 4, }; struct Mac_Object{ @@ -122,7 +122,7 @@ global Render_Target target; //////////////////////////////// function inline Plat_Handle -mac_to_plat_handle(Mac_Object* object){ +mac_to_plat_handle(Mac_Object *object){ Plat_Handle result = *(Plat_Handle*)(&object); return(result); } @@ -135,7 +135,7 @@ mac_to_object(Plat_Handle handle){ function mac_alloc_object(Mac_Object_Kind kind){ - Mac_Object* result = 0; + Mac_Object *result = 0; if (mac_vars.free_mac_objects.next != &mac_vars.free_mac_objects){ result = CastFromMember(Mac_Object, node, mac_vars.free_mac_objects.next); @@ -143,19 +143,19 @@ mac_alloc_object(Mac_Object_Kind kind){ if (!result){ i32 count = 512; - Mac_Object* objects = (Mac_Object*)system_memory_allocate(count * sizeof(Mac_Object), file_name_line_number); + Mac_Object *objects = (Mac_Object*)system_memory_allocate(count * sizeof(Mac_Object), file_name_line_number_lit_u8); - // NOTE(yuval): Link the first chain of the dll to the sentinel + // NOTE(yuval): Link the first node of the dll to the sentinel objects[0].node.prev = &mac_vars.free_mac_objects; mac_vars.free_mac_objects.next = &objects[0].node; - // NOTE(yuval): Link all dll chains to each other + // NOTE(yuval): Link all dll nodes to each other for (i32 chain_index = 1; chain_index < count; chain_index += 1){ objects[chain_index - 1].node.next = &objects[chain_index].node; objects[chain_index].node.prev = &objects[chain_index - 1].node; } - // NOTE(yuval): Link the last chain of the dll to the sentinel + // NOTE(yuval): Link the last node of the dll to the sentinel objects[count - 1].node.next = &mac_vars.free_mac_objects; mac_vars.free_mac_objects.prev = &objects[count - 1].node; @@ -170,6 +170,14 @@ mac_alloc_object(Mac_Object_Kind kind){ return(result); } +function +mac_free_object(Mac_Object *object){ + if (object->node.next != 0){ + dll_remove(&object->node); + } + + dll_insert(&mac_vars.free_mac_objects, &object->node); +} //////////////////////////////// @@ -188,7 +196,7 @@ mac_alloc_object(Mac_Object_Kind kind){ return YES; } -- (void)applicationWillTerminate:(NSNotification *)notification{ +- (void)applicationWillTerminate:(NSNotification*)notification{ } - (NSSize)windowWillResize:(NSWindow*)window toSize:(NSSize)frame_size{ @@ -207,10 +215,10 @@ int main(int arg_count, char **args){ @autoreleasepool{ // NOTE(yuval): Create NSApplication & Delegate - NSApplication* app = [NSApplication sharedApplication]; + NSApplication *app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - App_Delegate* app_delegate = [[App_Delegate alloc] init]; + App_Delegate *app_delegate = [[App_Delegate alloc] init]; [app setDelegate:app_delegate]; [NSApp finishLaunching]; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index a4009db7..b98780d8 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -82,7 +82,7 @@ mac_get_file_attributes(struct stat file_stat) { } function inline File_Attributes -mac_file_attributes_from_path(char* path) { +mac_file_attributes_from_path(char *path) { File_Attributes result = {}; struct stat file_stat; @@ -109,7 +109,7 @@ function system_get_file_list_sig(){ File_List result = {}; - u8* c_directory = push_array(arena, u8, directory.size + 1); + u8 *c_directory = push_array(arena, u8, directory.size + 1); block_copy(c_directory, directory.str, directory.size); c_directory[directory.size] = 0; @@ -129,7 +129,7 @@ system_get_file_list_sig(){ continue; } - File_Info* info = push_array(arena, File_Info, 1); + File_Info *info = push_array(arena, File_Info, 1); sll_queue_push(first, last, info); count += 1; @@ -146,8 +146,8 @@ system_get_file_list_sig(){ file_path_size += 1; } - char* file_path = push_array(arena, char, file_path_size + 1); - char* file_path_at = file_path; + char *file_path = push_array(arena, char, file_path_size + 1); + char *file_path_at = file_path; block_copy(file_path_at, directory.str, directory.size); file_path_at += directory.size; @@ -174,7 +174,7 @@ system_get_file_list_sig(){ result.count = count; i32 index = 0; - for (File_Info* node = first; + for (File_Info *node = first; node != 0; node = node->next){ result.infos[index] = node; @@ -189,7 +189,7 @@ function system_quick_file_attributes_sig(){ Temp_Memory temp = begin_temp(scratch); - char* c_file_name = push_array(scratch, char, file_name.size + 1); + char *c_file_name = push_array(scratch, char, file_name.size + 1); block_copy(c_file_name, file_name.str, file_name.size); c_file_name[file_name.size] = 0; @@ -273,9 +273,6 @@ system_save_file_sig(){ i32 fd = open(file_name, O_WRONLY | O_TRUNC | O_CREAT, 00640); if (fd != -1) { - u8* data_str = data.str; - u64 data_size = data.size; - do{ ssize_t bytes_written = write(fd, data.str, data.size); if (bytes_written == -1){ @@ -302,14 +299,14 @@ system_save_file_sig(){ //////////////////////////////// function inline System_Library -mac_to_system_library(void* dl_handle){ +mac_to_system_library(void *dl_handle){ System_Library result = *(System_Library*)(&dl_handle); return(result); } function inline void* mac_to_dl_handle(System_Library system_lib){ - void* result = *(void**)(&system_lib); + void *result = *(void**)(&system_lib); return(result); } @@ -317,13 +314,13 @@ function system_load_library_sig(){ b32 result = false; - void* lib = 0; + void *lib = 0; // NOTE(yuval): Open library handle { Temp_Memory temp = begin_temp(scratch); - char* c_file_name = push_array(scratch, char, file_name.size + 1); + char *c_file_name = push_array(scratch, char, file_name.size + 1); block_copy(c_file_name, file_name.str, file_name.size); c_file_name[file_name.size] = 0; @@ -342,7 +339,7 @@ system_load_library_sig(){ function system_release_library_sig(){ - void* lib = mac_to_dl_handle(handle); + void *lib = mac_to_dl_handle(handle); i32 rc = dlclose(lib); b32 result = (rc == 0); @@ -351,8 +348,8 @@ system_release_library_sig(){ function system_get_proc_sig(){ - void* lib = mac_to_dl_handle(handle); - Void_Func* result = (Void_Func*)dlsym(lib, proc_name); + void *lib = mac_to_dl_handle(handle); + Void_Func *result = (Void_Func*)dlsym(lib, proc_name); return(result); } @@ -364,9 +361,9 @@ system_now_time_sig(){ u64 now = mach_absolute_time(); // NOTE(yuval): Now time nanoseconds conversion - f64 now_nano = (f64)(((f64)now) * - ((f64)mac_vars.timebase_info.numer) / - ((f64)mac_vars.timebase_info.denom)); + f64 now_nano = (f64)((f64)now * + (f64)mac_vars.timebase_info.numer / + (f64)mac_vars.timebase_info.denom); // NOTE(yuval): Conversion to useconds u64 result = (u64)(now_nano * 1.0E-3); @@ -375,24 +372,36 @@ system_now_time_sig(){ function system_wake_up_timer_create_sig(){ - Plat_Handle result = {}; + Mac_Object *object = mac_alloc_object(MacObjectKind_Timer); + dll_insert(&mac_vars.timer_objects, &object->node); - NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.0 - target: view - selector: @selector(requestDisplay) - userInfo: nil repeats:NO]; + object->timer.timer = nil; + Plat_Handle result = mac_to_plat_handle(object); return(result); } function system_wake_up_timer_release_sig(){ - NotImplemented; + Mac_Object *object = (Mac_Object*)mac_to_object(handle); + if (object->kind == MacObjectKind_Timer){ + if ((object->timer != nil) && [object->timer isValid]) { + [object->timer invalidate]; + mac_free_object(object); + } + } } function system_wake_up_timer_set_sig(){ - NotImplemented; + Mac_Object *object = (Mac_Object*)mac_to_object(handle); + if (object->kind == MacObjectKind_Timer){ + f64 time_seconds = ((f64)time_milliseconds / 1000.0); + object->timer = [NSTimer scheduledTimerWithTimeInterval: time_seconds + target: mac_vars.view + selector: @selector(requestDisplay) + userInfo: nil repeats:NO]; + } } function From 50b8a646146f115eb072efabeebe9206f34a9e43 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 30 Dec 2019 02:33:30 +0200 Subject: [PATCH 27/67] The projects compiles. --- platform_mac/mac_4ed.mm | 48 +++++++++++++++++++++++++++---- platform_mac/mac_4ed_functions.mm | 6 ++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 9a87b069..aa0790b7 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -52,6 +52,7 @@ #import #include // NOTE(yuval): Used for proc_pidpath +#include // NOTE(yuval): Used for mach_absolute_time, mach_timebase_info, mach_timebase_info_data_t #include // NOTE(yuval): Used for opendir, readdir #include // NOTE(yuval): Used for dlopen, dlclose, dlsym @@ -83,6 +84,16 @@ //////////////////////////////// +@interface App_Delegate : NSObject +@end + +@interface Opengl_View : NSOpenGLView +@end + +//////////////////////////////// + +//////////////////////////////// + typedef i32 Mac_Object_Kind; enum{ MacObjectKind_ERROR = 0, @@ -110,6 +121,11 @@ struct Mac_Vars { String_Const_u8 binary_path; + NSWindow* window; + Opengl_View* view; + + mach_timebase_info_data_t timebase_info; + Node free_mac_objects; Node timer_objects; }; @@ -133,7 +149,7 @@ mac_to_object(Plat_Handle handle){ return(result); } -function +function Mac_Object* mac_alloc_object(Mac_Object_Kind kind){ Mac_Object *result = 0; @@ -170,7 +186,7 @@ mac_alloc_object(Mac_Object_Kind kind){ return(result); } -function +function void mac_free_object(Mac_Object *object){ if (object->node.next != 0){ dll_remove(&object->node); @@ -185,9 +201,6 @@ mac_free_object(Mac_Object *object){ //////////////////////////////// -@interface App_Delegate : NSObject -@end - @implementation App_Delegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -209,6 +222,28 @@ mac_free_object(Mac_Object *object){ } @end +@implementation Opengl_View +- (id)init { + self = [super init]; + return self; +} + +- (void)prepareOpenGL { + [super prepareOpenGL]; + [[self openGLContext] makeCurrentContext]; +} + +- (void)reshape { + [super reshape]; + + NSRect bounds = [self bounds]; + // [global_opengl_context makeCurrentContext]; + // [global_opengl_context update]; + // glViewport(0, 0, (GLsizei)bounds.size.width, + // (GLsizei)bounds.size.height); +} +@end + //////////////////////////////// int @@ -265,6 +300,9 @@ main(int arg_count, char **args){ dll_init_sentinel(&mac_vars.free_mac_objects); dll_init_sentinel(&mac_vars.timer_objects); + + // NOTE(yuval): Get the timebase info + mach_timebase_info(&mac_vars.timebase_info); #endif #if 0 diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index b98780d8..35e406e0 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -385,8 +385,8 @@ function system_wake_up_timer_release_sig(){ Mac_Object *object = (Mac_Object*)mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ - if ((object->timer != nil) && [object->timer isValid]) { - [object->timer invalidate]; + if ((object->timer.timer != nil) && [object->timer.timer isValid]) { + [object->timer.timer invalidate]; mac_free_object(object); } } @@ -397,7 +397,7 @@ system_wake_up_timer_set_sig(){ Mac_Object *object = (Mac_Object*)mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ f64 time_seconds = ((f64)time_milliseconds / 1000.0); - object->timer = [NSTimer scheduledTimerWithTimeInterval: time_seconds + object->timer.timer = [NSTimer scheduledTimerWithTimeInterval: time_seconds target: mac_vars.view selector: @selector(requestDisplay) userInfo: nil repeats:NO]; From 1d6be32462892f6a7dd5b3db28332232e2a7dd81 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 30 Dec 2019 03:21:21 +0200 Subject: [PATCH 28/67] Setup window and basic opengl view. --- platform_mac/mac_4ed.mm | 70 +++++++++++++++++++++---------- platform_mac/mac_4ed_functions.mm | 4 +- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index aa0790b7..15d73837 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -84,10 +84,11 @@ //////////////////////////////// -@interface App_Delegate : NSObject +@interface AppDelegate : NSObject @end -@interface Opengl_View : NSOpenGLView +@interface OpenGLView : NSOpenGLView +- (void)requestDisplay; @end //////////////////////////////// @@ -122,7 +123,7 @@ struct Mac_Vars { String_Const_u8 binary_path; NSWindow* window; - Opengl_View* view; + OpenGLView* view; mach_timebase_info_data_t timebase_info; @@ -201,7 +202,7 @@ mac_free_object(Mac_Object *object){ //////////////////////////////// -@implementation App_Delegate +@implementation AppDelegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -222,18 +223,18 @@ mac_free_object(Mac_Object *object){ } @end -@implementation Opengl_View +@implementation OpenGLView - (id)init { self = [super init]; return self; } -- (void)prepareOpenGL { +- (void)prepareOpenGL{ [super prepareOpenGL]; [[self openGLContext] makeCurrentContext]; } -- (void)reshape { +- (void)reshape{ [super reshape]; NSRect bounds = [self bounds]; @@ -242,35 +243,63 @@ mac_free_object(Mac_Object *object){ // glViewport(0, 0, (GLsizei)bounds.size.width, // (GLsizei)bounds.size.height); } + +- (void)requestDisplay{ + printf("Display Requested\n"); +} @end //////////////////////////////// int main(int arg_count, char **args){ + block_zero_struct(&mac_vars); + @autoreleasepool{ // NOTE(yuval): Create NSApplication & Delegate NSApplication *app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - App_Delegate *app_delegate = [[App_Delegate alloc] init]; + AppDelegate *app_delegate = [[AppDelegate alloc] init]; [app setDelegate:app_delegate]; - [NSApp finishLaunching]; + // NOTE(yuval): Create NSWindow + float w = 1280.0f; + float h = 720.0f; + NSRect screen_rect = [[NSScreen mainScreen] frame]; + NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); + + NSWindow* window = [[NSWindow alloc] initWithContentRect: initial_frame + styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable + backing: NSBackingStoreBuffered + defer: NO]; + + [window setBackgroundColor: NSColor.blackColor]; + [window setDelegate: app_delegate]; + [window setTitle: @"4coder"]; + [window makeKeyAndOrderFront: nil]; + + // NOTE(yuval): Create OpenGLView + NSView* content_view = [window contentView]; + + // TODO(yuval): Finish view setup! + mac_vars.view = [[OpenGLView alloc] init]; + [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [mac_vars.view setFrame:[content_view bounds]]; + + [content_view addSubview:mac_vars.view]; + + dll_init_sentinel(&mac_vars.free_mac_objects); + dll_init_sentinel(&mac_vars.timer_objects); Arena test_arena = make_arena_malloc(); - File_List list = system_get_file_list(&test_arena, - string_u8_litexpr("/Users/yuvaldolev/Desktop")); + Plat_Handle timer = system_wake_up_timer_create(); + system_wake_up_timer_set(timer, 5000); + + // NOTE(yuval): Start the app's run loop + [NSApp run]; + - for (u32 index = 0; index < list.count; ++index) { - File_Info* info = list.infos[index]; - - printf("File_Info{file_name:'%.*s', " - "attributes:{size:%llu, last_write_time:%llu, flags:{IsDirectory:%d}}}\n", - (i32)info->file_name.size, info->file_name.str, - info->attributes.size, info->attributes.last_write_time, - ((info->attributes.flags & FileAttribute_IsDirectory) != 0)); - } #if 0 // NOTE(yuval): Context Setup @@ -279,7 +308,6 @@ main(int arg_count, char **args){ get_base_allocator_system(), get_base_allocator_system()); - block_zero_struct(&mac_vars); mac_vars.tctx = &_tctx; API_VTable_system system_vtable = {}; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 35e406e0..f191f69d 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -563,9 +563,7 @@ system_condition_variable_free_sig(){ function system_memory_allocate_sig(){ - void* result = 0; - - NotImplemented; + void* result = malloc(size); return(result); } From 912174e7250add2af2293f953305a729260b0eac Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 31 Dec 2019 00:04:33 +0200 Subject: [PATCH 29/67] Experimentations with redrawing the opengl view. --- platform_mac/mac_4ed.mm | 84 ++++++++++++++++++++++++++----- platform_mac/mac_4ed_functions.mm | 8 +-- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 15d73837..db01684c 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -244,8 +244,42 @@ mac_free_object(Mac_Object *object){ // (GLsizei)bounds.size.height); } +- (void)drawRect:(NSRect)bounds{ + // [self getFrame]; + printf("Draw Rect!\n"); +} + +- (BOOL)acceptsFirstResponder{ + return YES; +} + +- (BOOL)becomeFirstResponder{ + return YES; +} + +- (BOOL)resignFirstResponder{ + return YES; +} + +- (void)keyDown:(NSEvent *)event{ + printf("Key Down!\n"); + [self requestDisplay]; +} + +- (void)mouseMoved:(NSEvent*)event{ + printf("Mouse Moved!\n"); + [self requestDisplay]; +} + +- (void)mouseDown:(NSEvent*)event{ + printf("Mouse Down!\n"); + [self requestDisplay]; +} + - (void)requestDisplay{ printf("Display Requested\n"); + + [self setNeedsDisplayInRect:[mac_vars.window frame]]; } @end @@ -269,25 +303,28 @@ main(int arg_count, char **args){ NSRect screen_rect = [[NSScreen mainScreen] frame]; NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); - NSWindow* window = [[NSWindow alloc] initWithContentRect: initial_frame - styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable - backing: NSBackingStoreBuffered - defer: NO]; + u32 style_mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; - [window setBackgroundColor: NSColor.blackColor]; - [window setDelegate: app_delegate]; - [window setTitle: @"4coder"]; - [window makeKeyAndOrderFront: nil]; + mac_vars.window = [[NSWindow alloc] initWithContentRect:initial_frame + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]; + + [mac_vars.window setBackgroundColor:NSColor.blackColor]; + [mac_vars.window setDelegate:app_delegate]; + [mac_vars.window setTitle:@"4coder"]; + [mac_vars.window setAcceptsMouseMovedEvents:YES]; // NOTE(yuval): Create OpenGLView - NSView* content_view = [window contentView]; + NSView* content_view = [mac_vars.window contentView]; // TODO(yuval): Finish view setup! mac_vars.view = [[OpenGLView alloc] init]; - [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [mac_vars.view setFrame:[content_view bounds]]; + [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [content_view addSubview:mac_vars.view]; + [mac_vars.window makeKeyAndOrderFront:nil]; dll_init_sentinel(&mac_vars.free_mac_objects); dll_init_sentinel(&mac_vars.timer_objects); @@ -297,9 +334,32 @@ main(int arg_count, char **args){ system_wake_up_timer_set(timer, 5000); // NOTE(yuval): Start the app's run loop +#if 1 [NSApp run]; - - +#else + for (;;) { + u64 count = 0; + + NSEvent* event; + do { + event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:nil//[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + + if (event != nil) { + // printf("Event: %lu\n", [event type]); + ++count; + } + + [NSApp sendEvent:event]; + } while (event != nil); + + if (count > 1) { + printf("Count: %llu\n", count); + } + } +#endif #if 0 // NOTE(yuval): Context Setup diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index f191f69d..f1938a76 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -397,10 +397,10 @@ system_wake_up_timer_set_sig(){ Mac_Object *object = (Mac_Object*)mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ f64 time_seconds = ((f64)time_milliseconds / 1000.0); - object->timer.timer = [NSTimer scheduledTimerWithTimeInterval: time_seconds - target: mac_vars.view - selector: @selector(requestDisplay) - userInfo: nil repeats:NO]; + object->timer.timer = [NSTimer scheduledTimerWithTimeInterval:time_seconds + target:mac_vars.view + selector:@selector(requestDisplay) + userInfo:nil repeats:NO]; } } From aff4a783e04cc01b3146e9413738395dc5b45457 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 31 Dec 2019 01:56:12 +0200 Subject: [PATCH 30/67] Implemented system_get_screen_scale_factor. --- platform_mac/mac_4ed.mm | 16 +++++++++++++--- platform_mac/mac_4ed_functions.mm | 15 +++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index db01684c..22947925 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -124,6 +124,7 @@ struct Mac_Vars { NSWindow* window; OpenGLView* view; + f32 screen_scale_factor; mach_timebase_info_data_t timebase_info; @@ -266,10 +267,12 @@ mac_free_object(Mac_Object *object){ [self requestDisplay]; } +/* - (void)mouseMoved:(NSEvent*)event{ printf("Mouse Moved!\n"); [self requestDisplay]; } +*/ - (void)mouseDown:(NSEvent*)event{ printf("Mouse Down!\n"); @@ -329,9 +332,16 @@ main(int arg_count, char **args){ dll_init_sentinel(&mac_vars.free_mac_objects); dll_init_sentinel(&mac_vars.timer_objects); - Arena test_arena = make_arena_malloc(); - Plat_Handle timer = system_wake_up_timer_create(); - system_wake_up_timer_set(timer, 5000); + // NOTE(yuval): Screen scale factor calculation + { + NSScreen* screen = [NSScreen mainScreen]; + NSDictionary* desc = [screen deviceDescription]; + NSSize size = [[desc valueForKey:NSDeviceResolution] sizeValue]; + f32 max_dpi = Max(size.width, size.height); + mac_vars.screen_scale_factor = (max_dpi / 72.0f); + } + + printf("screen scale factor: %f\n", system_get_screen_scale_factor()); // NOTE(yuval): Start the app's run loop #if 1 diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index f1938a76..1da70846 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -406,12 +406,18 @@ system_wake_up_timer_set_sig(){ function system_signal_step_sig(){ - NotImplemented; + [mac_vars.view requestDisplay]; } function system_sleep_sig(){ - NotImplemented; + u64 nanoseconds = (microseconds * Thousand(1)); + u64 abs_sleep_time = (u64)((f64)nanoseconds * + (f64)mac_vars.timebase_info.denom / + (f64)mac_vars.timebase_info.numer); + + u64 now = mach_absolute_time(); + mach_wait_until(now + abs_sleep_time); } //////////////////////////////// @@ -464,10 +470,7 @@ system_open_color_picker_sig(){ function system_get_screen_scale_factor_sig(){ - f32 result = 0.0f; - - NotImplemented; - + f32 result = mac_vars.screen_scale_factor; return(result); } From cbdc6a14e0fe2dc28b2ece5e5c237e901061cf80 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 31 Dec 2019 03:22:50 +0200 Subject: [PATCH 31/67] Implemented all system thread handling functions. --- platform_mac/mac_4ed.mm | 15 ++++++- platform_mac/mac_4ed_functions.mm | 73 ++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 22947925..f21d9d46 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -145,9 +145,22 @@ mac_to_plat_handle(Mac_Object *object){ return(result); } + +function inline System_Thread +mac_to_system_thread(Mac_Object *object){ + System_Thread result = *(System_Thread*)(&object); + return(result); +} + function inline Mac_Object* mac_to_object(Plat_Handle handle){ - Mac_Object* result = *(Mac_Object**)(&handle); + Mac_Object *result = *(Mac_Object**)(&handle); + return(result); +} + +function inline Mac_Object* +mac_to_object(System_Thread thread){ + Mac_Object *result = *(Mac_Object**)(&thread); return(result); } diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 1da70846..d099c2d8 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -383,7 +383,7 @@ system_wake_up_timer_create_sig(){ function system_wake_up_timer_release_sig(){ - Mac_Object *object = (Mac_Object*)mac_to_object(handle); + Mac_Object *object = mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ if ((object->timer.timer != nil) && [object->timer.timer isValid]) { [object->timer.timer invalidate]; @@ -394,7 +394,7 @@ system_wake_up_timer_release_sig(){ function system_wake_up_timer_set_sig(){ - Mac_Object *object = (Mac_Object*)mac_to_object(handle); + Mac_Object *object = mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ f64 time_seconds = ((f64)time_milliseconds / 1000.0); object->timer.timer = [NSTimer scheduledTimerWithTimeInterval:time_seconds @@ -476,44 +476,67 @@ system_get_screen_scale_factor_sig(){ //////////////////////////////// +function void* +mac_thread_wrapper(void *ptr){ + Mac_Object *object = (Mac_Object*)ptr; + Thread_Function *proc = object->thread.proc; + void *object_ptr = object->thread.ptr; + + pthread_mutex_lock(&mac_vars.thread_launch_mutex); + { + mac_vars.waiting_for_launch = false; + pthread_cond_signal(&mac_vars.thread_launch_cv); + } + pthread_mutex_unlock(&mac_vars.thread_launch_mutex); + + proc(object_ptr); + + return(0); +} + function system_thread_launch_sig(){ - System_Thread result = {}; + Mac_Object *object = mac_alloc_object(MacObjectKind_Thread); + object->thread.proc = proc; + object->thread.ptr = ptr; - NotImplemented; + pthread_mutex_lock(&mac_vars.thread_launch_mutex); + { + mac_vars.waiting_for_launch = true; + pthread_create(&object->thread.thread, 0, mac_thread_wrapper, object); + + while (mac_vars.waiting_for_launch){ + pthread_cond_wait(&mac_vars.thread_launch_cv, &mac_vars.thread_launch_mutex); + } + } + pthread_mutex_unlock(&mac_vars.thread_launch_mutex); + System_Thread result = mac_to_system_thread(object); return(result); } function system_thread_join_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(thread); + if (object->kind == MacObjectKind_Thread){ + pthread_join(object->thread.thread, 0); + } } function system_thread_free_sig(){ - NotImplemented; + Mac_Object* object = mac_to_object(thread); + if (object->kind == MacObjectKind_Thread){ + mac_free_object(object); + } } function system_thread_get_id_sig(){ - i32 result = 0; - - NotImplemented; - + i32 result = (i32)pthread_getthreadid_np(); return(result); } -function -system_acquire_global_frame_mutex_sig(){ - NotImplemented; -} - -function -system_release_global_frame_mutex_sig(){ - NotImplemented; -} - function system_mutex_make_sig(){ System_Mutex result = {}; @@ -538,6 +561,16 @@ system_mutex_free_sig(){ NotImplemented; } +function +system_acquire_global_frame_mutex_sig(){ + NotImplemented; +} + +function +system_release_global_frame_mutex_sig(){ + NotImplemented; +} + function system_condition_variable_make_sig(){ System_Condition_Variable result = {}; From 9263a4a59f1a5b89eac71056017a5228e3a17454 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 31 Dec 2019 13:14:05 +0200 Subject: [PATCH 32/67] Implemented all system mutex and cv handling functions. --- platform_mac/mac_4ed.mm | 55 +++++++++++++------------- platform_mac/mac_4ed_functions.mm | 66 +++++++++++++++++++++---------- 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index f21d9d46..0de68e77 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -58,6 +58,7 @@ #include // NOTE(yuval): Used for dlopen, dlclose, dlsym #include // NOTE(yuval): Used for errno #include // NOTE(yuval): Used for open +#include // NOTE(yuval): Used for threads, mutexes, cvs #include // NOTE(yuval): Used for getcwd, read, write, getpid #include // NOTE(yuval): Used for stat #include // NOTE(yuval): Used for struct stat, pid_t @@ -109,9 +110,16 @@ struct Mac_Object{ Mac_Object_Kind kind; union{ + NSTimer* timer; + struct{ - NSTimer* timer; - } timer; + pthread_t thread; + Thread_Function *proc; + void *ptr; + } thread; + + pthread_mutex_t mutex; + pthread_cond_t cv; }; }; @@ -130,6 +138,12 @@ struct Mac_Vars { Node free_mac_objects; Node timer_objects; + + pthread_mutex_t thread_launch_mutex; + pthread_cond_t thread_launch_cv; + b32 waiting_for_launch; + + System_Mutex global_frame_mutex; }; //////////////////////////////// @@ -139,31 +153,6 @@ global Render_Target target; //////////////////////////////// -function inline Plat_Handle -mac_to_plat_handle(Mac_Object *object){ - Plat_Handle result = *(Plat_Handle*)(&object); - return(result); -} - - -function inline System_Thread -mac_to_system_thread(Mac_Object *object){ - System_Thread result = *(System_Thread*)(&object); - return(result); -} - -function inline Mac_Object* -mac_to_object(Plat_Handle handle){ - Mac_Object *result = *(Mac_Object**)(&handle); - return(result); -} - -function inline Mac_Object* -mac_to_object(System_Thread thread){ - Mac_Object *result = *(Mac_Object**)(&thread); - return(result); -} - function Mac_Object* mac_alloc_object(Mac_Object_Kind kind){ Mac_Object *result = 0; @@ -210,6 +199,18 @@ mac_free_object(Mac_Object *object){ dll_insert(&mac_vars.free_mac_objects, &object->node); } +function inline Plat_Handle +mac_to_plat_handle(Mac_Object *object){ + Plat_Handle result = *(Plat_Handle*)(&object); + return(result); +} + +function inline Mac_Object* +mac_to_object(Plat_Handle handle){ + Mac_Object *result = *(Mac_Object**)(&handle); + return(result); +} + //////////////////////////////// #import "mac_4ed_functions.mm" diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index d099c2d8..082d3094 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -375,7 +375,7 @@ system_wake_up_timer_create_sig(){ Mac_Object *object = mac_alloc_object(MacObjectKind_Timer); dll_insert(&mac_vars.timer_objects, &object->node); - object->timer.timer = nil; + object->timer = nil; Plat_Handle result = mac_to_plat_handle(object); return(result); @@ -385,8 +385,8 @@ function system_wake_up_timer_release_sig(){ Mac_Object *object = mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ - if ((object->timer.timer != nil) && [object->timer.timer isValid]) { - [object->timer.timer invalidate]; + if ((object->timer != nil) && [object->timer isValid]) { + [object->timer invalidate]; mac_free_object(object); } } @@ -397,7 +397,7 @@ system_wake_up_timer_set_sig(){ Mac_Object *object = mac_to_object(handle); if (object->kind == MacObjectKind_Timer){ f64 time_seconds = ((f64)time_milliseconds / 1000.0); - object->timer.timer = [NSTimer scheduledTimerWithTimeInterval:time_seconds + object->timer = [NSTimer scheduledTimerWithTimeInterval:time_seconds target:mac_vars.view selector:@selector(requestDisplay) userInfo:nil repeats:NO]; @@ -511,7 +511,7 @@ system_thread_launch_sig(){ } pthread_mutex_unlock(&mac_vars.thread_launch_mutex); - System_Thread result = mac_to_system_thread(object); + System_Thread result = mac_to_plat_handle(object); return(result); } @@ -533,66 +533,92 @@ system_thread_free_sig(){ function system_thread_get_id_sig(){ - i32 result = (i32)pthread_getthreadid_np(); + pthread_t id = pthread_self(); + i32 result = *(i32*)(&id); return(result); } function system_mutex_make_sig(){ - System_Mutex result = {}; - - NotImplemented; + Mac_Object *object = mac_alloc_object(MacObjectKind_Mutex); + pthread_mutex_init(&object->mutex, 0); + System_Mutex result = mac_to_plat_handle(object); return(result); } function system_mutex_acquire_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(mutex); + if (object->kind == MacObjectKind_Mutex){ + pthread_mutex_lock(&object->mutex); + } } function system_mutex_release_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(mutex); + if (object->kind == MacObjectKind_Mutex){ + pthread_mutex_unlock(&object->mutex); + } } function system_mutex_free_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(mutex); + if (object->kind == MacObjectKind_Mutex){ + pthread_mutex_destroy(&object->mutex); + mac_free_object(object); + } } function system_acquire_global_frame_mutex_sig(){ - NotImplemented; + if (tctx->kind == ThreadKind_AsyncTasks){ + system_mutex_acquire(mac_vars.global_frame_mutex); + } } function system_release_global_frame_mutex_sig(){ - NotImplemented; + if (tctx->kind == ThreadKind_AsyncTasks){ + system_mutex_release(mac_vars.global_frame_mutex); + } } function system_condition_variable_make_sig(){ - System_Condition_Variable result = {}; - - NotImplemented; + Mac_Object *object = mac_alloc_object(MacObjectKind_CV); + pthread_cond_init(&object->cv, 0); + System_Condition_Variable result = mac_to_plat_handle(object); return(result); } function system_condition_variable_wait_sig(){ - NotImplemented; + Mac_Object *object_cv = mac_to_object(cv); + Mac_Object *object_mutex = mac_to_object(mutex); + if ((object_cv->kind == MacObjectKind_CV) && (object_mutex->kind == MacObjectKind_Mutex)){ + pthread_cond_wait(&object_cv->cv, &object_mutex->mutex); + } } function system_condition_variable_signal_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(cv); + if (object->kind == MacObjectKind_CV){ + pthread_cond_signal(&object->cv); + } } function system_condition_variable_free_sig(){ - NotImplemented; + Mac_Object *object = mac_to_object(cv); + if (object->kind == MacObjectKind_CV){ + pthread_cond_destroy(&object->cv); + mac_free_object(object); + } } //////////////////////////////// From 8c7600ba09ad637e810d81b5cab7f3a89b245d77 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 31 Dec 2019 23:36:12 +0200 Subject: [PATCH 33/67] Implemented all system memory handling functions. --- platform_mac/mac_4ed.mm | 15 ++-- platform_mac/mac_4ed_functions.mm | 121 ++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 16 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 0de68e77..ed67979f 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -60,6 +60,7 @@ #include // NOTE(yuval): Used for open #include // NOTE(yuval): Used for threads, mutexes, cvs #include // NOTE(yuval): Used for getcwd, read, write, getpid +#include // NOTE(yuval): Used for mmap, munmap, mprotect #include // NOTE(yuval): Used for stat #include // NOTE(yuval): Used for struct stat, pid_t @@ -359,29 +360,23 @@ main(int arg_count, char **args){ // NOTE(yuval): Start the app's run loop #if 1 + printf("Running using NSApp run\n"); [NSApp run]; #else + printf("Running using manual event loop\n"); + for (;;) { u64 count = 0; NSEvent* event; do { event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:nil//[NSDate distantFuture] + untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event != nil) { - // printf("Event: %lu\n", [event type]); - ++count; - } - [NSApp sendEvent:event]; } while (event != nil); - - if (count > 1) { - printf("Count: %llu\n", count); - } } #endif diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 082d3094..4ef30169 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -623,32 +623,141 @@ system_condition_variable_free_sig(){ //////////////////////////////// +struct Memory_Annotation_Tracker_Node{ + Memory_Annotation_Tracker_Node *next; + Memory_Annotation_Tracker_Node *prev; + String_Const_u8 location; + u64 size; +}; + +struct Memory_Annotation_Tracker{ + Memory_Annotation_Tracker_Node *first; + Memory_Annotation_Tracker_Node *last; + i32 count; +}; + +global Memory_Annotation_Tracker memory_tracker = {}; +global pthread_mutex_t memory_tracker_mutex; + +global_const u64 ALLOCATION_SIZE_ADJUSTMENT = 64; + +function void* +mac_memory_allocate_extended(void *base, u64 size, String_Const_u8 location){ + u64 adjusted_size = size + ALLOCATION_SIZE_ADJUSTMENT; + void *memory = mmap(base, adjusted_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)memory; + + pthread_mutex_lock(&memory_tracker_mutex); + { + zdll_push_back(memory_tracker.first, memory_tracker.last, node); + memory_tracker.count += 1; + } + pthread_mutex_unlock(&memory_tracker_mutex); + + node->location = location; + node->size = size; + + void* result = (node + 1); + return(result); +} + +function void +mac_memory_free_extended(void *ptr){ + Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)ptr; + node -= 1; + + pthread_mutex_lock(&memory_tracker_mutex); + { + zdll_remove(memory_tracker.first, memory_tracker.last, node); + memory_tracker.count -= 1; + } + pthread_mutex_unlock(&memory_tracker_mutex); + + munmap(node, node->size + ALLOCATION_SIZE_ADJUSTMENT); +} + function system_memory_allocate_sig(){ - void* result = malloc(size); - + void* result = mac_memory_allocate_extended(0, size, location); return(result); } function system_memory_set_protection_sig(){ - b32 result = false; + b32 result = true; - NotImplemented; + int protect = 0; + switch (flags & 0x7){ + case 0: + { + protect = PROT_NONE; + } break; + + case MemProtect_Read: + { + protect = PROT_READ; + } break; + + case MemProtect_Write: + case MemProtect_Read | MemProtect_Write: + { + protect = PROT_READ | PROT_WRITE; + } break; + + case MemProtect_Execute: + { + protect = PROT_EXEC; + } break; + + case MemProtect_Execute | MemProtect_Read: + { + protect = PROT_READ | PROT_EXEC; + } break; + + // NOTE(inso): some W^X protection things might be unhappy about this one + case MemProtect_Execute | MemProtect_Write: + case MemProtect_Execute | MemProtect_Write | MemProtect_Read: + { + protect = PROT_READ | PROT_WRITE | PROT_EXEC; + } break; + } + + Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)ptr; + node -= 1; + + if(mprotect(node, size, protect) == -1){ + result = false; + } return(result); } function system_memory_free_sig(){ - NotImplemented; + mac_memory_free_extended(ptr); } function system_memory_annotation_sig(){ Memory_Annotation result = {}; - NotImplemented; + pthread_mutex_lock(&memory_tracker_mutex); + { + for (Memory_Annotation_Tracker_Node *node = memory_tracker.first; + node != 0; + node = node->next){ + Memory_Annotation_Node *r_node = push_array(arena, Memory_Annotation_Node, 1); + sll_queue_push(result.first, result.last, r_node); + result.count += 1; + + r_node->location = node->location; + r_node->address = node + 1; + r_node->size = node->size; + } + + } + pthread_mutex_unlock(&memory_tracker_mutex); return(result); } From 26fd4cd230396d3b1e8081638dce1ce22094da46 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 1 Jan 2020 00:07:21 +0200 Subject: [PATCH 34/67] Finished most of the system api functions (a few will be implemented later). --- platform_mac/mac_4ed.mm | 38 +++++++++++++++++++++++++++++++ platform_mac/mac_4ed_functions.mm | 21 +++++++---------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index ed67979f..90463bc6 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -71,6 +71,37 @@ #define global static #define external extern "C" +struct Control_Keys{ + b8 l_ctrl; + b8 r_ctrl; + b8 l_alt; + b8 r_alt; +}; + +struct Mac_Input_Chunk_Transient{ + Input_List event_list; + b8 mouse_l_press; + b8 mouse_l_release; + b8 mouse_r_press; + b8 mouse_r_release; + b8 out_of_window; + i8 mouse_wheel; + b8 trying_to_kill; +}; + +struct Mac_Input_Chunk_Persistent{ + Vec2_i32 mouse; + Control_Keys controls; + Input_Modifier_Set_Fixed modifiers; + b8 mouse_l; + b8 mouse_r; +}; + +struct Mac_Input_Chunk{ + Mac_Input_Chunk_Transient trans; + Mac_Input_Chunk_Persistent pers; +}; + //////////////////////////////// #define SLASH '/' @@ -128,6 +159,13 @@ struct Mac_Vars { Thread_Context *tctx; Arena* frame_arena; + Mac_Input_Chunk input_chunk; + + b8 full_screen; + b8 do_toggle; + + i32 cursor_show; + i32 prev_cursor_show; String_Const_u8 binary_path; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 4ef30169..bae982e8 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -766,33 +766,28 @@ system_memory_annotation_sig(){ function system_show_mouse_cursor_sig(){ - NotImplemented; + mac_vars.cursor_show = show; } function system_set_fullscreen_sig(){ - b32 result = false; + // NOTE(yuval): Read comment in system_set_fullscreen_sig in win32_4ed.cpp + mac_vars.do_toggle = (mac_vars.full_screen != full_screen); - NotImplemented; - - return(result); + b32 success = true; + return(success); } function system_is_fullscreen_sig(){ - b32 result = false; - - NotImplemented; - + // NOTE(yuval): Read comment in system_is_fullscreen_sig in win32_4ed.cpp + b32 result = (mac_vars.full_screen != mac_vars.do_toggle); return(result); } function system_get_keyboard_modifiers_sig(){ - Input_Modifier_Set result = {}; - - NotImplemented; - + Input_Modifier_Set result = copy_modifier_set(arena, &mac_vars.input_chunk.pers.modifiers); return(result); } From 0cfb8b319ce15d2184d9149685a350a4c13edc27 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 1 Jan 2020 04:16:39 +0200 Subject: [PATCH 35/67] Work on setting up the opengl view. --- platform_mac/mac_4ed.mm | 78 +++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 90463bc6..33b75381 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -121,6 +121,7 @@ struct Mac_Input_Chunk{ @end @interface OpenGLView : NSOpenGLView +- (void)init_opengl; - (void)requestDisplay; @end @@ -156,6 +157,8 @@ struct Mac_Object{ }; struct Mac_Vars { + b32 gl_is_initialized; + Thread_Context *tctx; Arena* frame_arena; @@ -266,26 +269,39 @@ mac_to_object(Plat_Handle handle){ - (void)applicationWillTerminate:(NSNotification*)notification{ } - -- (NSSize)windowWillResize:(NSWindow*)window toSize:(NSSize)frame_size{ - // frame_size.height = ((f32)frame_size.width / global_aspect_ratio); - return frame_size; -} - -- (void)windowWillClose:(id)sender{ - // global_running = false; -} @end @implementation OpenGLView -- (id)init { +- (id)init{ self = [super init]; + if (self == nil){ + return nil; + } + + [self init_opengl]; + return self; } -- (void)prepareOpenGL{ +- (void)dealloc +{ + [super dealloc]; +} + +- (void)prepareOpenGL +{ [super prepareOpenGL]; + [[self openGLContext] makeCurrentContext]; + + // NOTE(yuval): Setup vsync + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; +} + +- (void)awakeFromNib +{ + [self init_gl]; } - (void)reshape{ @@ -332,6 +348,44 @@ mac_to_object(Plat_Handle handle){ [self requestDisplay]; } +- (void)init_opengl{ + if (mac_vars.gl_is_initialized){ + return; + } + + [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // NOTE(yuval): Setup OpenGL + NSOpenGLPixelFormatAttribute opengl_attrs[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + // NSOpenGLPFASampleBuffers, 1, + // NSOpenGLPFASamples, 16, + 0 + }; + + NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:opengl_attrs]; + if (pixel_format == nil){ + fprintf(stderr, "Error creating OpenGLPixelFormat\n"); + exit(1); + } + + NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil]; + + [self setPixelFormat:format]; + [self setOpenGLContext:context]; + + [context makeCurrentContext]; + + [pixel_format release]; + + mac_vars.gl_is_initialized = true; +} + - (void)requestDisplay{ printf("Display Requested\n"); @@ -345,6 +399,8 @@ int main(int arg_count, char **args){ block_zero_struct(&mac_vars); + mac_vars.gl_is_initialized = false; + @autoreleasepool{ // NOTE(yuval): Create NSApplication & Delegate NSApplication *app = [NSApplication sharedApplication]; From debdda8184e937a47501a2d59cf3239f684f5526 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 1 Jan 2020 18:38:48 +0200 Subject: [PATCH 36/67] Started working on getting the opengl renderer to work but encountered a few errors on the way... --- bin/4ed_build.cpp | 1 + opengl/4ed_opengl_defines.h | 4 +- platform_mac/mac_4ed.mm | 312 +++++++++++++++++++++++++++--------- 3 files changed, 243 insertions(+), 74 deletions(-) diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 146169fb..03a9a1b2 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -467,6 +467,7 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha fm_finish_build_line(&line); Temp_Dir temp = fm_pushdir(out_path); + // systemf("clang++ %s -E -o %s", line.build_options, "4ed.i"); systemf("clang++ %s -o %s", line.build_options, out_file); fm_popdir(temp); } diff --git a/opengl/4ed_opengl_defines.h b/opengl/4ed_opengl_defines.h index be20f65e..558b1e98 100644 --- a/opengl/4ed_opengl_defines.h +++ b/opengl/4ed_opengl_defines.h @@ -12,7 +12,9 @@ #if !defined(FRED_OPENGL_DEFINES_H) #define FRED_OPENGL_DEFINES_H -#include +// NOTE(yuval): This file does not exist on MacOS. +// opengl has been included from the platform layer +// #include #define GL_TEXTURE_MAX_LEVEL 0x813D diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 33b75381..148108d0 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -54,6 +54,10 @@ #include // NOTE(yuval): Used for proc_pidpath #include // NOTE(yuval): Used for mach_absolute_time, mach_timebase_info, mach_timebase_info_data_t +#define GL_GLEXT_LEGACY +#include +#include + #include // NOTE(yuval): Used for opendir, readdir #include // NOTE(yuval): Used for dlopen, dlclose, dlsym #include // NOTE(yuval): Used for errno @@ -71,6 +75,9 @@ #define global static #define external extern "C" +// NOTE(yuval): This is a hack to fix the CALL_CONVENTION not being defined problem in 4coder_base_types.h +#define CALL_CONVENTION + struct Control_Keys{ b8 l_ctrl; b8 r_ctrl; @@ -172,6 +179,8 @@ struct Mac_Vars { String_Const_u8 binary_path; + String_Const_u8 clipboard_contents; + NSWindow* window; OpenGLView* view; f32 screen_scale_factor; @@ -186,6 +195,8 @@ struct Mac_Vars { b32 waiting_for_launch; System_Mutex global_frame_mutex; + + Log_Function *log_string; }; //////////////////////////////// @@ -255,10 +266,39 @@ mac_to_object(Plat_Handle handle){ //////////////////////////////// +#include "4ed_font_provider_freetype.h" +#include "4ed_font_provider_freetype.h" + +#include "opengl/4ed_opengl_render.cpp" + #import "mac_4ed_functions.mm" //////////////////////////////// +function void +mac_error_box(char *msg, b32 shutdown = true){ + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + + NSString *title_string = @"Error"; + NSString *message_string = [NSString stringWithUTF8String:msg]; + [alert setMessageText:title_string]; + [alert setInformativeText:message_string]; + + [alert runModal]; + + if (shutdown){ + exit(1); + } +} + +function b32 +mac_file_can_be_made(u8* filename){ + b32 result = access((char*)filename, W_OK) == 0; + return(result); +} + +//////////////////////////////// + @implementation AppDelegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -301,17 +341,14 @@ mac_to_object(Plat_Handle handle){ - (void)awakeFromNib { - [self init_gl]; + [self init_opengl]; } - (void)reshape{ [super reshape]; NSRect bounds = [self bounds]; - // [global_opengl_context makeCurrentContext]; - // [global_opengl_context update]; - // glViewport(0, 0, (GLsizei)bounds.size.width, - // (GLsizei)bounds.size.height); + // mac_resize(rect.size.width, rect.size.height); } - (void)drawRect:(NSRect)bounds{ @@ -319,6 +356,11 @@ mac_to_object(Plat_Handle handle){ printf("Draw Rect!\n"); } +- (BOOL)windowShouldClose:(NSWindow*)sender{ + // osx_try_to_close(); + return(NO); +} + - (BOOL)acceptsFirstResponder{ return YES; } @@ -332,19 +374,14 @@ mac_to_object(Plat_Handle handle){ } - (void)keyDown:(NSEvent *)event{ - printf("Key Down!\n"); [self requestDisplay]; } -/* - (void)mouseMoved:(NSEvent*)event{ - printf("Mouse Moved!\n"); [self requestDisplay]; } -*/ - (void)mouseDown:(NSEvent*)event{ - printf("Mouse Down!\n"); [self requestDisplay]; } @@ -374,9 +411,9 @@ mac_to_object(Plat_Handle handle){ exit(1); } - NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil]; + NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:nil]; - [self setPixelFormat:format]; + [self setPixelFormat:pixel_format]; [self setOpenGLContext:context]; [context makeCurrentContext]; @@ -397,50 +434,47 @@ mac_to_object(Plat_Handle handle){ int main(int arg_count, char **args){ - block_zero_struct(&mac_vars); - - mac_vars.gl_is_initialized = false; - @autoreleasepool{ // NOTE(yuval): Create NSApplication & Delegate - NSApplication *app = [NSApplication sharedApplication]; + NSApplication *ns_app = [NSApplication sharedApplication]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; AppDelegate *app_delegate = [[AppDelegate alloc] init]; - [app setDelegate:app_delegate]; + [ns_app setDelegate:app_delegate]; - // NOTE(yuval): Create NSWindow - float w = 1280.0f; - float h = 720.0f; - NSRect screen_rect = [[NSScreen mainScreen] frame]; - NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); + pthread_mutex_init(&memory_tracker_mutex, 0); - u32 style_mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + // NOTE(yuval): Context Setup + Thread_Context _tctx = {}; + thread_ctx_init(&_tctx, ThreadKind_Main, + get_base_allocator_system(), + get_base_allocator_system()); - mac_vars.window = [[NSWindow alloc] initWithContentRect:initial_frame - styleMask:style_mask - backing:NSBackingStoreBuffered - defer:NO]; + block_zero_struct(&mac_vars); + mac_vars.tctx = &_tctx; - [mac_vars.window setBackgroundColor:NSColor.blackColor]; - [mac_vars.window setDelegate:app_delegate]; - [mac_vars.window setTitle:@"4coder"]; - [mac_vars.window setAcceptsMouseMovedEvents:YES]; + API_VTable_system system_vtable = {}; + system_api_fill_vtable(&system_vtable); - // NOTE(yuval): Create OpenGLView - NSView* content_view = [mac_vars.window contentView]; + API_VTable_graphics graphics_vtable = {}; + graphics_api_fill_vtable(&graphics_vtable); - // TODO(yuval): Finish view setup! - mac_vars.view = [[OpenGLView alloc] init]; - [mac_vars.view setFrame:[content_view bounds]]; - [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + API_VTable_font font_vtable = {}; + font_api_fill_vtable(&font_vtable); - [content_view addSubview:mac_vars.view]; - [mac_vars.window makeKeyAndOrderFront:nil]; + // NOTE(yuval): Memory + mac_vars.frame_arena = reserve_arena(mac_vars.tctx); + target.arena = make_arena_system(KB(256)); + + mac_vars.cursor_show = MouseCursorShow_Always; + mac_vars.prev_cursor_show = MouseCursorShow_Always; dll_init_sentinel(&mac_vars.free_mac_objects); dll_init_sentinel(&mac_vars.timer_objects); + pthread_mutex_init(&mac_vars.thread_launch_mutex, 0); + pthread_cond_init(&mac_vars.thread_launch_cv, 0); + // NOTE(yuval): Screen scale factor calculation { NSScreen* screen = [NSScreen mainScreen]; @@ -450,7 +484,171 @@ main(int arg_count, char **args){ mac_vars.screen_scale_factor = (max_dpi / 72.0f); } - printf("screen scale factor: %f\n", system_get_screen_scale_factor()); + // NOTE(yuval): Load core + System_Library core_library = {}; + App_Functions app = {}; + { + App_Get_Functions *get_funcs = 0; + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + Path_Search_List search_list = {}; + search_list_add_system_path(scratch, &search_list, SystemPath_Binary); + + String_Const_u8 core_path = get_full_path(scratch, &search_list, SCu8("4ed_app.so")); + if (system_load_library(scratch, core_path, &core_library)){ + get_funcs = (App_Get_Functions*)system_get_proc(core_library, "app_get_functions"); + if (get_funcs != 0){ + app = get_funcs(); + } + else{ + char msg[] = "Failed to get application code from '4ed_app.so'."; + mac_error_box(msg); + } + } + else{ + char msg[] = "Could not load '4ed_app.so'. This file should be in the same directory as the main '4ed' executable."; + mac_error_box(msg); + } + } + + // NOTE(yuval): Send api vtables to core + app.load_vtables(&system_vtable, &font_vtable, &graphics_vtable); + mac_vars.log_string = app.get_logger(); + + // NOTE(yuval): Init & command line parameters + Plat_Settings plat_settings = {}; + void *base_ptr = 0; + { + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); + curdir = string_mod_replace_character(curdir, '\\', '/'); + char **files = 0; + i32 *file_count = 0; + base_ptr = app.read_command_line(mac_vars.tctx, curdir, &plat_settings, &files, &file_count, arg_count, args); + { + i32 end = *file_count; + i32 i = 0, j = 0; + for (; i < end; ++i){ + if (mac_file_can_be_made((u8*)files[i])){ + files[j] = files[i]; + ++j; + } + } + *file_count = j; + } + } + + // NOTE(yuval): Load custom layer + System_Library custom_library = {}; + Custom_API custom = {}; + { + char custom_not_found_msg[] = "Did not find a library for the custom layer."; + char custom_fail_version_msg[] = "Failed to load custom code due to missing version information or a version mismatch. Try rebuilding with buildsuper."; + char custom_fail_init_apis[] = "Failed to load custom code due to missing 'init_apis' symbol. Try rebuilding with buildsuper"; + + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + String_Const_u8 default_file_name = string_u8_litexpr("custom_4coder.so"); + Path_Search_List search_list = {}; + search_list_add_system_path(scratch, &search_list, SystemPath_CurrentDirectory); + search_list_add_system_path(scratch, &search_list, SystemPath_Binary); + String_Const_u8 custom_file_names[2] = {}; + i32 custom_file_count = 1; + if (plat_settings.custom_dll != 0){ + custom_file_names[0] = SCu8(plat_settings.custom_dll); + if (!plat_settings.custom_dll_is_strict){ + custom_file_names[1] = default_file_name; + custom_file_count += 1; + } + } + else{ + custom_file_names[0] = default_file_name; + } + String_Const_u8 custom_file_name = {}; + for (i32 i = 0; i < custom_file_count; i += 1){ + custom_file_name = get_full_path(scratch, &search_list, custom_file_names[i]); + if (custom_file_name.size > 0){ + break; + } + } + b32 has_library = false; + if (custom_file_name.size > 0){ + if (system_load_library(scratch, custom_file_name, &custom_library)){ + has_library = true; + } + } + + if (!has_library){ + mac_error_box(custom_not_found_msg); + } + custom.get_version = (_Get_Version_Type*)system_get_proc(custom_library, "get_version"); + if (custom.get_version == 0 || custom.get_version(MAJOR, MINOR, PATCH) == 0){ + mac_error_box(custom_fail_version_msg); + } + custom.init_apis = (_Init_APIs_Type*)system_get_proc(custom_library, "init_apis"); + if (custom.init_apis == 0){ + mac_error_box(custom_fail_init_apis); + } + } + + // + // Window and GL View Initialization + // + + // NOTE(yuval): Create NSWindow + float w; + float h; + if (plat_settings.set_window_size){ + w = (float)plat_settings.window_w; + h = (float)plat_settings.window_h; + } else{ + w = 800.0f; + h = 800.0f; + } + + NSRect screen_rect = [[NSScreen mainScreen] frame]; + NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); + + u32 style_mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + + mac_vars.window = [[NSWindow alloc] initWithContentRect:initial_frame + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]; + + [mac_vars.window setMinSize:NSMakeSize(100, 100)]; + [mac_vars.window setBackgroundColor:NSColor.blackColor]; + [mac_vars.window setDelegate:app_delegate]; + [mac_vars.window setTitle:@"4coder"]; + [mac_vars.window setAcceptsMouseMovedEvents:YES]; + + // NOTE(yuval): Create OpenGLView + NSView* content_view = [mac_vars.window contentView]; + + mac_vars.gl_is_initialized = false; + + mac_vars.view = [[OpenGLView alloc] init]; + [mac_vars.view setFrame:[content_view bounds]]; + [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // NOTE(yuval): Display opengl view and window + [content_view addSubview:mac_vars.view]; + [mac_vars.window makeKeyAndOrderFront:nil]; + + // + // TODO(yuval): Misc System Initializations + + // NOTE(yuval): Get the timebase info + mach_timebase_info(&mac_vars.timebase_info); + + // + // App init + // + + { + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); + curdir = string_mod_replace_character(curdir, '\\', '/'); + app.init(mac_vars.tctx, &target, base_ptr, mac_vars.clipboard_contents, curdir, custom); + } // NOTE(yuval): Start the app's run loop #if 1 @@ -474,38 +672,6 @@ main(int arg_count, char **args){ } #endif -#if 0 - // NOTE(yuval): Context Setup - Thread_Context _tctx = {}; - thread_ctx_init(&_tctx, ThreadKind_Main, - get_base_allocator_system(), - get_base_allocator_system()); - - mac_vars.tctx = &_tctx; - - API_VTable_system system_vtable = {}; - system_api_fill_vtable(&system_vtable); - - API_VTable_graphics graphics_vtable = {}; - graphics_api_fill_vtable(&graphics_vtable); - - API_VTable_font font_vtable = {}; - font_api_fill_vtable(&font_vtable); - - // NOTE(yuval): Memory - mac_vars.frame_arena = reserve_arena(mac_vars.tctx); - target.arena = make_arena_system(KB(256)); - - mac_vars.cursor_show = MouseCursorShow_Always; - mac_vars.prev_cursor_show = MouseCursorShow_Always; - - dll_init_sentinel(&mac_vars.free_mac_objects); - dll_init_sentinel(&mac_vars.timer_objects); - - // NOTE(yuval): Get the timebase info - mach_timebase_info(&mac_vars.timebase_info); -#endif - #if 0 // NOTE(yuval): Application Core Update Application_Step_Result result = {}; From 6f827801e5132abaaa29d6db204d76844708b13b Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 2 Jan 2020 01:13:32 +0200 Subject: [PATCH 37/67] macOS platform layer initialization and very basic update functions. The 4coder beta has been rendered successfully for the first time on macOS! --- custom/4coder_base_types.h | 5 +- .../Resources/DWARF/metadata_generator | Bin 136740 -> 136740 bytes opengl/4ed_opengl_defines.h | 7 -- opengl/4ed_opengl_render.cpp | 19 ++-- platform_mac/mac_4ed.mm | 95 +++++++++++------- platform_mac/mac_4ed_functions.mm | 20 ++-- platform_mac/mac_4ed_opengl.mm | 23 +++++ platform_mac/mac_4ed_opengl_funcs.h | 17 ++++ 8 files changed, 120 insertions(+), 66 deletions(-) create mode 100644 platform_mac/mac_4ed_opengl.mm create mode 100644 platform_mac/mac_4ed_opengl_funcs.h diff --git a/custom/4coder_base_types.h b/custom/4coder_base_types.h index 20517f7f..6811fe58 100644 --- a/custom/4coder_base_types.h +++ b/custom/4coder_base_types.h @@ -144,12 +144,13 @@ #endif #endif +// NOTE(yuval): Changed this so that CALL_CONVENTION will be defined for all platforms #if OS_WINDOWS # if ARCH_32BIT # define CALL_CONVENTION __stdcall -# else -# define CALL_CONVENTION # endif +#else +# define CALL_CONVENTION #endif #if defined(JUST_GUESS_INTS) diff --git a/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator b/custom/metadata_generator.dSYM/Contents/Resources/DWARF/metadata_generator index 133e453d1c931a6805c0b6e109b54aada975ee7d..815fd87182819f59da407a6ef16a81b7f4629669 100644 GIT binary patch delta 1990 zcmY+Fdr;I>6vyx7cYgR2cDb7)gp3Re{h`iSP#H@r3e7YPGsPp3G?C%qLJnAimXuL0 zr9%(2hlM{IA7v6+B8Y>os6D1J>@{gGUd|8#W;r=I{o_(2yZ3Wm_uO;O zz1!AdwRKocJ#rx3{Sn0;5<=X-j}F@t>2itew$m~&bcQN2q&M1EZnz6)KwDvmIMc$Q z>VcbCRA~sc5tAT8l_7-MgxN)o6|h5@vPynnC;8-D*%k8n=w2siyC9+v2wHtczMg`eH^iMPlvblc_*&|D3 zQQcY6)%er{(Pvnx0Qz$beORd&Fvii&(R@`2Sbr1XbIMRP0PXVyWu@c-=qvIz&k`bm zO%h&5PYOr=^_1N1&XMtVMC!ho)V07-q&&%C&O0KJ+6-!A$pw29e!*`i6~g3`bD6`? zdFg3VtB~MK&a07x`wFN23|VnzA_ny{jN)1MEVlM1ActWy>MmmT;uFhd*+?!k6%!AY zlRgeV8&yP>y^?~KF!`8V<|3FTQhh|-*28qFTQ;C>Q^)lluby^m3rdLiu8L%C`vU;&3aPI;<59N`2N zscyhstO-%QIunMI^pxrY@b;;fnx-%KX?oGA${^I#PBGkZ6Fh&3%zQs0xy;{zS@g1V zpLFmR?BZWYbq?FG3yCApy}BK{hz96(dj;=GH0r0})zHWH$iAq8A85MYek74H$c67G z@7wX{A@0IS(ZHmLJ5HorWvf9)B+~u?eaC$=6#D*NxiCM;OZvsscPACeO}&w6_M9EE zIQXWaMv{f-Tmzq0L%8#JveQ7zcrdtEFxrOj0c}ISxBB_ph08M)>Mg?(YF?%gr`UQo zRJQ^2-u3|Q1pLIBy@0y`6u@tvf$U>~T=*Q8Z8KbEAtoLwb=J8b8d@E>P8rs5nU&h8 z#xc6a_wkWPqqW*-gEp#ljQ(Jwx=5pS+GtCr2%>pM4RFNA`$xa4SA~!_8acY z)zA+(DGWF%=yDDs1z7{#kdwktM0JKEQ@Hs56h?FkejmRJCk-VNgwW4kFMPU@n1)k^ vr3!sww$q}g4UakjmA(vTLhrZA;V79G+PX`Y8M4G4+bu82{LtAwvSQPJn!iU% delta 2006 zcmY+Fe@vBC7{|}yeO`QpdpS1-44I6JZKaz9o5m$Xp{*60@}s~~F!_xWYoRlWs4iwB zi)^LA>r!VXVkSSN$g7mAm0`0Vx=bKih)A(qF4sSQFJipF!p7We@ zJG-sUZmVTLj-3(_(41S-;{H`2G>a2L*jw%iagriDQ@ z0AFBHg(1{anD`kg4Ixw$WyVs|)P$EM!w?{{yxA4vM}G=?ARovllIs^Q$_gf9@pM8pg-vg5rQQ ztcei3D^sj%A*hxu(z+3%Lo&zuI|T2@ITpcNM>r^z6$5DGaOw7uWirpdjZ_J+i$yP@ z4r?*Fz_6Vx;WEoH_0mRCwU{V36Y(I#vw=9}ESqB`n8Dm9hB-ol6fek=BwLz(gN(DO^qg^zvoM z<#Y(v*+bc~Dq+C9fi^I?kkJt_LL_svV}TXRWvfg&Z0N_CN?ei67rsM+X9C=$~g-BqN zgg4QX!jXR+CJ&0f35wcc#gbcWznxXU{b>&Sog)&dt)NbpoVO=(WW4`EP$5h{NtZbW zotK(Ol_KG}oaeJ7+|#_3X0l?;L=5Uz7{#;hIc)VWKn}z6I0q56AD?J0(^hhsshD_Z zJ?Z1gVx#i#W2hj%B}_gxm$?+CiS%Sx9UEXe(>Zq zBGnJLhczdtL46W}06n9I0K9!VNiDM%evo!JRT+hv4pS6&;1thykeQ!_NG|gaU?#od z+%FycRqWzFNOcZv*oDLq=-zz|yNCqn_S=bfCKC12@Ot3W`{YnW!9|+wzaL1XjB?>G zk@rA6dWyR;C>jomxC2GXHMSaYL}J0epzpa)Mgte`my7e0Qb@m``Yw5q+{~MqYR}sx zi~YL|HJ&U)Z#8`0GK4#iC+!AW#)HBAC}W2qe83JvzrXr9v~ihcLhUguq3+BOVvw!h zhU#vB-rHWl62N8F>;v2jpc?%48^}J&&xOxnS)1W93o-Ffsk6>XXs9fFopo5pWmamV zTF2-*-_N>mqYc_ZC9lR-GTiQy9B7g`af_Sw4Oj0)~!=p|>r7y$BfurqmEJ8jQcxjJ(%#bDa - #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_MULTISAMPLE 0x809D @@ -226,9 +222,6 @@ typedef void GL_Debug_Function(GLenum src, void *user_data); typedef GL_Debug_Function *GLDEBUGPROC; -#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0; -#include "4ed_opengl_funcs.h" - #endif // BOTTOM diff --git a/opengl/4ed_opengl_render.cpp b/opengl/4ed_opengl_render.cpp index 3e83095d..e3413ae0 100644 --- a/opengl/4ed_opengl_render.cpp +++ b/opengl/4ed_opengl_render.cpp @@ -9,8 +9,6 @@ // TOP -#include "4ed_opengl_defines.h" - internal void gl__bind_texture(Render_Target *t, i32 texid){ if (t->bound_texture != texid){ @@ -225,11 +223,16 @@ gl_render(Render_Target *t){ #if !SHIP_MODE glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, 0, false); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, 0, false); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, 0, true); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, 0, true); - glDebugMessageCallback(gl__error_callback, 0); + if (glDebugMessageControl){ + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, 0, false); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, 0, false); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, 0, true); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, 0, true); + } + + if (glDebugMessageCallback){ + glDebugMessageCallback(gl__error_callback, 0); + } #endif //////////////////////////////// @@ -256,7 +259,7 @@ gl_render(Render_Target *t){ //////////////////////////////// - { + { t->fallback_texture_id = gl__get_texture(V3i32(2, 2, 1), TextureKind_Mono); u8 white_block[] = { 0xFF, 0xFF, 0xFF, 0xFF, }; gl__fill_texture(TextureKind_Mono, 0, V3i32(0, 0, 0), V3i32(2, 2, 1), white_block); diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 148108d0..3886cdcd 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -54,10 +54,6 @@ #include // NOTE(yuval): Used for proc_pidpath #include // NOTE(yuval): Used for mach_absolute_time, mach_timebase_info, mach_timebase_info_data_t -#define GL_GLEXT_LEGACY -#include -#include - #include // NOTE(yuval): Used for opendir, readdir #include // NOTE(yuval): Used for dlopen, dlclose, dlsym #include // NOTE(yuval): Used for errno @@ -75,9 +71,6 @@ #define global static #define external extern "C" -// NOTE(yuval): This is a hack to fix the CALL_CONVENTION not being defined problem in 4coder_base_types.h -#define CALL_CONVENTION - struct Control_Keys{ b8 l_ctrl; b8 r_ctrl; @@ -168,9 +161,12 @@ struct Mac_Vars { Thread_Context *tctx; - Arena* frame_arena; + Arena *frame_arena; Mac_Input_Chunk input_chunk; + void *base_ptr; + u64 timer_start; + b8 full_screen; b8 do_toggle; @@ -181,8 +177,8 @@ struct Mac_Vars { String_Const_u8 clipboard_contents; - NSWindow* window; - OpenGLView* view; + NSWindow *window; + OpenGLView *view; f32 screen_scale_factor; mach_timebase_info_data_t timebase_info; @@ -203,6 +199,7 @@ struct Mac_Vars { global Mac_Vars mac_vars; global Render_Target target; +global App_Functions app; //////////////////////////////// @@ -266,10 +263,12 @@ mac_to_object(Plat_Handle handle){ //////////////////////////////// -#include "4ed_font_provider_freetype.h" -#include "4ed_font_provider_freetype.h" +#include +#include +#import "mac_4ed_opengl.mm" -#include "opengl/4ed_opengl_render.cpp" +#include "4ed_font_provider_freetype.h" +#include "4ed_font_provider_freetype.cpp" #import "mac_4ed_functions.mm" @@ -297,6 +296,14 @@ mac_file_can_be_made(u8* filename){ return(result); } +function void +mac_resize(float width, float height){ + if ((width > 0.0f) && (height > 0.0f)){ + target.width = width; + target.height = height; + } +} + //////////////////////////////// @implementation AppDelegate @@ -323,13 +330,11 @@ mac_file_can_be_made(u8* filename){ return self; } -- (void)dealloc -{ +- (void)dealloc{ [super dealloc]; } -- (void)prepareOpenGL -{ +- (void)prepareOpenGL{ [super prepareOpenGL]; [[self openGLContext] makeCurrentContext]; @@ -339,8 +344,7 @@ mac_file_can_be_made(u8* filename){ [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; } -- (void)awakeFromNib -{ +- (void)awakeFromNib{ [self init_opengl]; } @@ -352,8 +356,23 @@ mac_file_can_be_made(u8* filename){ } - (void)drawRect:(NSRect)bounds{ - // [self getFrame]; - printf("Draw Rect!\n"); + // NOTE(yuval): Read comment in win32_4ed.cpp's main loop + system_mutex_release(mac_vars.global_frame_mutex); + + Application_Step_Input input = {}; + + // NOTE(yuval): Application Core Update + Application_Step_Result result = {}; + if (app.step != 0){ + result = app.step(mac_vars.tctx, &target, mac_vars.base_ptr, &input); + } + + CGLLockContext([[self openGLContext] CGLContextObj]); + [[self openGLContext] makeCurrentContext]; + gl_render(&target); + [[self openGLContext] flushBuffer]; + CGLUnlockContext([[self openGLContext] CGLContextObj]); + } - (BOOL)windowShouldClose:(NSWindow*)sender{ @@ -424,8 +443,6 @@ mac_file_can_be_made(u8* filename){ } - (void)requestDisplay{ - printf("Display Requested\n"); - [self setNeedsDisplayInRect:[mac_vars.window frame]]; } @end @@ -486,7 +503,6 @@ main(int arg_count, char **args){ // NOTE(yuval): Load core System_Library core_library = {}; - App_Functions app = {}; { App_Get_Functions *get_funcs = 0; Scratch_Block scratch(mac_vars.tctx, Scratch_Share); @@ -516,14 +532,14 @@ main(int arg_count, char **args){ // NOTE(yuval): Init & command line parameters Plat_Settings plat_settings = {}; - void *base_ptr = 0; + mac_vars.base_ptr = 0; { Scratch_Block scratch(mac_vars.tctx, Scratch_Share); String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); curdir = string_mod_replace_character(curdir, '\\', '/'); char **files = 0; i32 *file_count = 0; - base_ptr = app.read_command_line(mac_vars.tctx, curdir, &plat_settings, &files, &file_count, arg_count, args); + mac_vars.base_ptr = app.read_command_line(mac_vars.tctx, curdir, &plat_settings, &files, &file_count, arg_count, args); { i32 end = *file_count; i32 i = 0, j = 0; @@ -593,6 +609,9 @@ main(int arg_count, char **args){ // Window and GL View Initialization // + // NOTE(yuval): Load OpenGL functions + mac_gl_load_functions(); + // NOTE(yuval): Create NSWindow float w; float h; @@ -601,9 +620,11 @@ main(int arg_count, char **args){ h = (float)plat_settings.window_h; } else{ w = 800.0f; - h = 800.0f; + h = 600.0f; } + mac_resize(w, h); + NSRect screen_rect = [[NSScreen mainScreen] frame]; NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); @@ -635,6 +656,7 @@ main(int arg_count, char **args){ // // TODO(yuval): Misc System Initializations + // // NOTE(yuval): Get the timebase info mach_timebase_info(&mac_vars.timebase_info); @@ -647,9 +669,18 @@ main(int arg_count, char **args){ Scratch_Block scratch(mac_vars.tctx, Scratch_Share); String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); curdir = string_mod_replace_character(curdir, '\\', '/'); - app.init(mac_vars.tctx, &target, base_ptr, mac_vars.clipboard_contents, curdir, custom); + app.init(mac_vars.tctx, &target, mac_vars.base_ptr, mac_vars.clipboard_contents, curdir, custom); } + // + // Main loop + // + + mac_vars.global_frame_mutex = system_mutex_make(); + system_mutex_acquire(mac_vars.global_frame_mutex); + + mac_vars.timer_start = system_now_time(); + // NOTE(yuval): Start the app's run loop #if 1 printf("Running using NSApp run\n"); @@ -671,13 +702,5 @@ main(int arg_count, char **args){ } while (event != nil); } #endif - -#if 0 - // NOTE(yuval): Application Core Update - Application_Step_Result result = {}; - if (app.step != 0){ - result = app.step(mac_vars.tctx, &target, base_ptr, &input); - } -#endif } } \ No newline at end of file diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index bae982e8..46543bab 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -406,7 +406,10 @@ system_wake_up_timer_set_sig(){ function system_signal_step_sig(){ - [mac_vars.view requestDisplay]; + [NSTimer scheduledTimerWithTimeInterval:0.0 + target:mac_vars.view + selector:@selector(requestDisplay) + userInfo:nil repeats:NO]; } function @@ -801,19 +804,13 @@ system_get_keyboard_modifiers_sig(){ function graphics_get_texture_sig(){ - u32 result = 0; - - NotImplemented; - + u32 result = gl__get_texture(dim, texture_kind); return(result); } function graphics_fill_texture_sig(){ - b32 result = false; - - NotImplemented; - + b32 result = gl__fill_texture(texture_kind, texture, p, dim, data); return(result); } @@ -827,10 +824,7 @@ graphics_fill_texture_sig(){ function font_make_face_sig(){ - Face* result = 0; - - NotImplemented; - + Face* result = ft__font_make_face(arena, description, scale_factor); return(result); } diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm new file mode 100644 index 00000000..b87492ba --- /dev/null +++ b/platform_mac/mac_4ed_opengl.mm @@ -0,0 +1,23 @@ +/* Mac OpenGL layer for 4coder */ + +#include "opengl/4ed_opengl_defines.h" + +#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0; +#include "mac_4ed_opengl_funcs.h" + +#include "opengl/4ed_opengl_render.cpp" + +function b32 +mac_gl_load_functions(){ + b32 result = true; + + // NOTE(yuval): Open the gl dynamic library + void* gl_image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY); + + // NOTE(yuval): Load gl functions +#define GL_FUNC(f,R,P) Stmnt((f) = (f##_Function*)dlsym(gl_image, #f); \ + (result) &= (f != 0);); +#include "mac_4ed_opengl_funcs.h" + + return result; +} diff --git a/platform_mac/mac_4ed_opengl_funcs.h b/platform_mac/mac_4ed_opengl_funcs.h new file mode 100644 index 00000000..649847f9 --- /dev/null +++ b/platform_mac/mac_4ed_opengl_funcs.h @@ -0,0 +1,17 @@ +/* Mac OpenGL functions for 4coder */ + +// TOP +/* Usage: +#define GL_FUNC(N,R,P) ~~~~ +#include "4ed_opengl_funcs.h" +*/ + +GL_FUNC(glDebugMessageControl, void, (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled)) +GL_FUNC(glDebugMessageCallback, void, (GLDEBUGPROC callback, const void *userParam)) + +GL_FUNC(glGenVertexArrays, void, (GLsizei n, GLuint *arrays)) +GL_FUNC(glBindVertexArray, void, (GLuint array)) + +GL_FUNC(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer)) + +#undef GL_FUNC From 0420a9a852a396394b75fb8ce15c420941ecaae3 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 2 Jan 2020 02:21:33 +0200 Subject: [PATCH 38/67] Implemented resizing (seems to be really slow for some reason...) and basic input (actually we're not receiving user input yet, but the we're sending the app an empty input struct with only a few parameters being updated like if it's the first step.). --- platform_mac/mac_4ed.mm | 51 +++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 3886cdcd..85e988d6 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1,5 +1,8 @@ /* Mac Objective-C layer for 4coder */ +#define FPS 60 +#define frame_useconds (1000000 / FPS) + #include "4coder_base_types.h" #include "4coder_version.h" #include "4coder_events.h" @@ -164,11 +167,9 @@ struct Mac_Vars { Arena *frame_arena; Mac_Input_Chunk input_chunk; - void *base_ptr; - u64 timer_start; - b8 full_screen; b8 do_toggle; + b32 send_exit_signal; i32 cursor_show; i32 prev_cursor_show; @@ -182,6 +183,9 @@ struct Mac_Vars { f32 screen_scale_factor; mach_timebase_info_data_t timebase_info; + b32 first; + void *base_ptr; + u64 timer_start; Node free_mac_objects; Node timer_objects; @@ -302,6 +306,8 @@ mac_resize(float width, float height){ target.width = width; target.height = height; } + + system_signal_step(0); } //////////////////////////////// @@ -352,15 +358,42 @@ mac_resize(float width, float height){ [super reshape]; NSRect bounds = [self bounds]; - // mac_resize(rect.size.width, rect.size.height); + mac_resize(bounds.size.width, bounds.size.height); } - (void)drawRect:(NSRect)bounds{ // NOTE(yuval): Read comment in win32_4ed.cpp's main loop system_mutex_release(mac_vars.global_frame_mutex); + // NOTE(yuval): Prepare the Frame Input + Mac_Input_Chunk input_chunk = mac_vars.input_chunk; Application_Step_Input input = {}; + input.first_step = mac_vars.first; + input.dt = frame_useconds / 1000000.0f; + input.events = input_chunk.trans.event_list; + + input.mouse.out_of_window = input_chunk.trans.out_of_window; + + input.mouse.l = input_chunk.pers.mouse_l; + input.mouse.press_l = input_chunk.trans.mouse_l_press; + input.mouse.release_l = input_chunk.trans.mouse_l_release; + + input.mouse.r = input_chunk.pers.mouse_r; + input.mouse.press_r = input_chunk.trans.mouse_r_press; + input.mouse.release_r = input_chunk.trans.mouse_r_release; + + input.mouse.wheel = input_chunk.trans.mouse_wheel; + input.mouse.p = input_chunk.pers.mouse; + + input.trying_to_kill = input_chunk.trans.trying_to_kill; + + // NOTE(yuval): See comment in win32_4ed.cpp's main loop + if (mac_vars.send_exit_signal){ + input.trying_to_kill = true; + mac_vars.send_exit_signal = false; + } + // NOTE(yuval): Application Core Update Application_Step_Result result = {}; if (app.step != 0){ @@ -373,10 +406,12 @@ mac_resize(float width, float height){ [[self openGLContext] flushBuffer]; CGLUnlockContext([[self openGLContext] CGLContextObj]); + mac_vars.first = false; } - (BOOL)windowShouldClose:(NSWindow*)sender{ // osx_try_to_close(); + printf("Window Closing!\n"); return(NO); } @@ -419,8 +454,8 @@ mac_resize(float width, float height){ NSOpenGLPFAColorSize, 32, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADepthSize, 24, - // NSOpenGLPFASampleBuffers, 1, - // NSOpenGLPFASamples, 16, + NSOpenGLPFASampleBuffers, 1, + NSOpenGLPFASamples, 16, 0 }; @@ -638,7 +673,7 @@ main(int arg_count, char **args){ [mac_vars.window setMinSize:NSMakeSize(100, 100)]; [mac_vars.window setBackgroundColor:NSColor.blackColor]; [mac_vars.window setDelegate:app_delegate]; - [mac_vars.window setTitle:@"4coder"]; + [mac_vars.window setTitle:@WINDOW_NAME]; [mac_vars.window setAcceptsMouseMovedEvents:YES]; // NOTE(yuval): Create OpenGLView @@ -676,6 +711,8 @@ main(int arg_count, char **args){ // Main loop // + mac_vars.first = true; + mac_vars.global_frame_mutex = system_mutex_make(); system_mutex_acquire(mac_vars.global_frame_mutex); From d1b9977a634fc3e085c579a46b8bf05b708446fd Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 2 Jan 2020 04:37:23 +0200 Subject: [PATCH 39/67] Started working on high-dpi support. --- platform_mac/mac_4ed.mm | 74 ++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 85e988d6..7136ae2e 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -162,6 +162,8 @@ struct Mac_Object{ struct Mac_Vars { b32 gl_is_initialized; + i32 width, height; + Thread_Context *tctx; Arena *frame_arena; @@ -303,8 +305,14 @@ mac_file_can_be_made(u8* filename){ function void mac_resize(float width, float height){ if ((width > 0.0f) && (height > 0.0f)){ - target.width = width; - target.height = height; + NSSize coord_size = NSMakeSize(width, height); + NSSize backing_size = [mac_vars.view convertSizeToBacking:coord_size]; + + mac_vars.width = (i32)backing_size.width; + mac_vars.height = (i32)backing_size.height; + + target.width = (i32)backing_size.width; + target.height = (i32)backing_size.height; } system_signal_step(0); @@ -361,6 +369,20 @@ mac_resize(float width, float height){ mac_resize(bounds.size.width, bounds.size.height); } +- (void)viewDidChangeBackingProperties{ + + // NOTE(yuval): Screen scale factor calculation + NSScreen* screen = [NSScreen mainScreen]; + NSDictionary* desc = [screen deviceDescription]; + NSSize size = [[desc valueForKey:NSDeviceResolution] sizeValue]; + f32 max_dpi = Max(size.width, size.height); + mac_vars.screen_scale_factor = (max_dpi / 72.0f); + + NSRect frame = [screen frame]; + printf("Screen: w:%f h:%f\n", frame.size.width, frame.size.height); + printf("Scale Factor: %f\n\n", mac_vars.screen_scale_factor); +} + - (void)drawRect:(NSRect)bounds{ // NOTE(yuval): Read comment in win32_4ed.cpp's main loop system_mutex_release(mac_vars.global_frame_mutex); @@ -388,6 +410,8 @@ mac_resize(float width, float height){ input.trying_to_kill = input_chunk.trans.trying_to_kill; + block_zero_struct(&mac_vars.input_chunk.trans); + // NOTE(yuval): See comment in win32_4ed.cpp's main loop if (mac_vars.send_exit_signal){ input.trying_to_kill = true; @@ -407,6 +431,8 @@ mac_resize(float width, float height){ CGLUnlockContext([[self openGLContext] CGLContextObj]); mac_vars.first = false; + + linalloc_clear(mac_vars.frame_arena); } - (BOOL)windowShouldClose:(NSWindow*)sender{ @@ -428,15 +454,30 @@ mac_resize(float width, float height){ } - (void)keyDown:(NSEvent *)event{ - [self requestDisplay]; + NSString* characters = [event characters]; + if ([characters length] != 0) { + u32 character_code = [characters characterAtIndex:0]; + + // NOTE(yuval): Control characters generate character_codes < 32 + if (character_code > 31) { + // TODO(yuval): This is actually in utf16!!! + String_Const_u32 str_32 = SCu32(&character_code, 1); + String_Const_u8 str_8 = string_u8_from_string_u32(mac_vars.frame_arena, str_32).string; + + Input_Event event = {}; + event.kind = InputEventKind_TextInsert; + event.text.string = str_8; + push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list, &event); + + system_signal_step(0); + } + } } - (void)mouseMoved:(NSEvent*)event{ - [self requestDisplay]; } - (void)mouseDown:(NSEvent*)event{ - [self requestDisplay]; } - (void)init_opengl{ @@ -478,7 +519,9 @@ mac_resize(float width, float height){ } - (void)requestDisplay{ - [self setNeedsDisplayInRect:[mac_vars.window frame]]; + CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); + NSRect rect = NSRectFromCGRect(cg_rect); + [self setNeedsDisplayInRect:rect]; } @end @@ -648,20 +691,18 @@ main(int arg_count, char **args){ mac_gl_load_functions(); // NOTE(yuval): Create NSWindow - float w; - float h; + i32 w; + i32 h; if (plat_settings.set_window_size){ - w = (float)plat_settings.window_w; - h = (float)plat_settings.window_h; + w = plat_settings.window_w; + h = plat_settings.window_h; } else{ - w = 800.0f; - h = 600.0f; + w = 800; + h = 600; } - mac_resize(w, h); - NSRect screen_rect = [[NSScreen mainScreen] frame]; - NSRect initial_frame = NSMakeRect((screen_rect.size.width - w) * 0.5f, (screen_rect.size.height - h) * 0.5f, w, h); + NSRect initial_frame = NSMakeRect((f32)(screen_rect.size.width - w) * 0.5f, (f32)(screen_rect.size.height - h) * 0.5f, w, h); u32 style_mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; @@ -684,11 +725,14 @@ main(int arg_count, char **args){ mac_vars.view = [[OpenGLView alloc] init]; [mac_vars.view setFrame:[content_view bounds]]; [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [mac_vars.view setWantsBestResolutionOpenGLSurface:YES]; // NOTE(yuval): Display opengl view and window [content_view addSubview:mac_vars.view]; [mac_vars.window makeKeyAndOrderFront:nil]; + mac_resize(w, h); + // // TODO(yuval): Misc System Initializations // From 4e0549f270338cf57f8cbb733131f3cdc31a5580 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 3 Jan 2020 00:09:22 +0200 Subject: [PATCH 40/67] OpenGL renderer abstraction. The OpenGL renderer is now in its own view that's separate from the main 4coder view which handles all input and updates. Also added a Window Delegate which handled window events. --- platform_mac/mac_4ed.mm | 174 +++++++++++------------------- platform_mac/mac_4ed_functions.mm | 8 +- platform_mac/mac_4ed_opengl.mm | 127 ++++++++++++++++++++-- 3 files changed, 184 insertions(+), 125 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 7136ae2e..958d1399 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -120,11 +120,13 @@ struct Mac_Input_Chunk{ //////////////////////////////// -@interface AppDelegate : NSObject +@interface FCoderAppDelegate : NSObject @end -@interface OpenGLView : NSOpenGLView -- (void)init_opengl; +@interface FCoderWindowDelegate : NSObject +@end + +@interface FCoderView : NSView - (void)requestDisplay; @end @@ -160,8 +162,6 @@ struct Mac_Object{ }; struct Mac_Vars { - b32 gl_is_initialized; - i32 width, height; Thread_Context *tctx; @@ -181,7 +181,7 @@ struct Mac_Vars { String_Const_u8 clipboard_contents; NSWindow *window; - OpenGLView *view; + FCoderView *view; f32 screen_scale_factor; mach_timebase_info_data_t timebase_info; @@ -318,69 +318,61 @@ mac_resize(float width, float height){ system_signal_step(0); } +function inline void +mac_resize(NSWindow *window){ + NSRect bounds = [[window contentView] bounds]; + mac_resize(bounds.size.width, bounds.size.height); +} + //////////////////////////////// -@implementation AppDelegate +// TODO(yuval): mac_resize(bounds.size.width, bounds.size.height); + +@implementation FCoderAppDelegate - (void)applicationDidFinishLaunching:(id)sender{ } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender{ - return YES; + return(YES); } - (void)applicationWillTerminate:(NSNotification*)notification{ } @end -@implementation OpenGLView +@implementation FCoderWindowDelegate +- (BOOL)windowShouldClose:(id)sender { + mac_vars.input_chunk.trans.trying_to_kill = true; + system_signal_step(0); + + return(NO); +} + +- (void)windowDidResize:(NSNotification*)notification { + mac_resize(mac_vars.window); +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { +} +@end + +@implementation FCoderView - (id)init{ self = [super init]; - if (self == nil){ - return nil; - } - - [self init_opengl]; - - return self; + return(self); } - (void)dealloc{ [super dealloc]; } -- (void)prepareOpenGL{ - [super prepareOpenGL]; - - [[self openGLContext] makeCurrentContext]; - - // NOTE(yuval): Setup vsync - GLint swapInt = 1; - [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; -} - -- (void)awakeFromNib{ - [self init_opengl]; -} - -- (void)reshape{ - [super reshape]; - - NSRect bounds = [self bounds]; - mac_resize(bounds.size.width, bounds.size.height); -} - - (void)viewDidChangeBackingProperties{ - // NOTE(yuval): Screen scale factor calculation - NSScreen* screen = [NSScreen mainScreen]; - NSDictionary* desc = [screen deviceDescription]; - NSSize size = [[desc valueForKey:NSDeviceResolution] sizeValue]; - f32 max_dpi = Max(size.width, size.height); - mac_vars.screen_scale_factor = (max_dpi / 72.0f); - - NSRect frame = [screen frame]; - printf("Screen: w:%f h:%f\n", frame.size.width, frame.size.height); - printf("Scale Factor: %f\n\n", mac_vars.screen_scale_factor); + printf("Backing changed!\n"); + mac_resize(mac_vars.window); } - (void)drawRect:(NSRect)bounds{ @@ -424,33 +416,29 @@ mac_resize(float width, float height){ result = app.step(mac_vars.tctx, &target, mac_vars.base_ptr, &input); } - CGLLockContext([[self openGLContext] CGLContextObj]); - [[self openGLContext] makeCurrentContext]; - gl_render(&target); - [[self openGLContext] flushBuffer]; - CGLUnlockContext([[self openGLContext] CGLContextObj]); + // NOTE(yuval): Quit the app + if (result.perform_kill){ + printf("Terminating 4coder!\n"); + [NSApp terminate:nil]; + } + + mac_gl_render(&target); mac_vars.first = false; linalloc_clear(mac_vars.frame_arena); } -- (BOOL)windowShouldClose:(NSWindow*)sender{ - // osx_try_to_close(); - printf("Window Closing!\n"); - return(NO); -} - - (BOOL)acceptsFirstResponder{ - return YES; + return(YES); } - (BOOL)becomeFirstResponder{ - return YES; + return(YES); } - (BOOL)resignFirstResponder{ - return YES; + return(YES); } - (void)keyDown:(NSEvent *)event{ @@ -480,44 +468,6 @@ mac_resize(float width, float height){ - (void)mouseDown:(NSEvent*)event{ } -- (void)init_opengl{ - if (mac_vars.gl_is_initialized){ - return; - } - - [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - // NOTE(yuval): Setup OpenGL - NSOpenGLPixelFormatAttribute opengl_attrs[] = { - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAAccelerated, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 32, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 24, - NSOpenGLPFASampleBuffers, 1, - NSOpenGLPFASamples, 16, - 0 - }; - - NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:opengl_attrs]; - if (pixel_format == nil){ - fprintf(stderr, "Error creating OpenGLPixelFormat\n"); - exit(1); - } - - NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:nil]; - - [self setPixelFormat:pixel_format]; - [self setOpenGLContext:context]; - - [context makeCurrentContext]; - - [pixel_format release]; - - mac_vars.gl_is_initialized = true; -} - - (void)requestDisplay{ CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); @@ -531,11 +481,13 @@ int main(int arg_count, char **args){ @autoreleasepool{ // NOTE(yuval): Create NSApplication & Delegate - NSApplication *ns_app = [NSApplication sharedApplication]; + [NSApplication sharedApplication]; + Assert(NSApp != nil); + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - AppDelegate *app_delegate = [[AppDelegate alloc] init]; - [ns_app setDelegate:app_delegate]; + FCoderAppDelegate *app_delegate = [[FCoderAppDelegate alloc] init]; + [NSApp setDelegate:app_delegate]; pthread_mutex_init(&memory_tracker_mutex, 0); @@ -687,10 +639,7 @@ main(int arg_count, char **args){ // Window and GL View Initialization // - // NOTE(yuval): Load OpenGL functions - mac_gl_load_functions(); - - // NOTE(yuval): Create NSWindow + // NOTE(yuval): Create Window & Window Delegate i32 w; i32 h; if (plat_settings.set_window_size){ @@ -711,23 +660,24 @@ main(int arg_count, char **args){ backing:NSBackingStoreBuffered defer:NO]; + FCoderWindowDelegate *window_delegate = [[FCoderWindowDelegate alloc] init]; + [mac_vars.window setDelegate:window_delegate]; + [mac_vars.window setMinSize:NSMakeSize(100, 100)]; [mac_vars.window setBackgroundColor:NSColor.blackColor]; - [mac_vars.window setDelegate:app_delegate]; [mac_vars.window setTitle:@WINDOW_NAME]; [mac_vars.window setAcceptsMouseMovedEvents:YES]; - // NOTE(yuval): Create OpenGLView NSView* content_view = [mac_vars.window contentView]; - mac_vars.gl_is_initialized = false; + // NOTE(yuval): Initialize the renderer + mac_gl_init(mac_vars.window); - mac_vars.view = [[OpenGLView alloc] init]; + // NOTE(yuval): Create the 4coder view + mac_vars.view = [[FCoderView alloc] init]; [mac_vars.view setFrame:[content_view bounds]]; [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [mac_vars.view setWantsBestResolutionOpenGLSurface:YES]; - // NOTE(yuval): Display opengl view and window [content_view addSubview:mac_vars.view]; [mac_vars.window makeKeyAndOrderFront:nil]; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 46543bab..aa3f619d 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -362,8 +362,8 @@ system_now_time_sig(){ // NOTE(yuval): Now time nanoseconds conversion f64 now_nano = (f64)((f64)now * - (f64)mac_vars.timebase_info.numer / - (f64)mac_vars.timebase_info.denom); + ((f64)mac_vars.timebase_info.numer / + (f64)mac_vars.timebase_info.denom)); // NOTE(yuval): Conversion to useconds u64 result = (u64)(now_nano * 1.0E-3); @@ -416,8 +416,8 @@ function system_sleep_sig(){ u64 nanoseconds = (microseconds * Thousand(1)); u64 abs_sleep_time = (u64)((f64)nanoseconds * - (f64)mac_vars.timebase_info.denom / - (f64)mac_vars.timebase_info.numer); + ((f64)mac_vars.timebase_info.denom / + (f64)mac_vars.timebase_info.numer)); u64 now = mach_absolute_time(); mach_wait_until(now + abs_sleep_time); diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index b87492ba..dd89c079 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -7,17 +7,126 @@ #include "opengl/4ed_opengl_render.cpp" -function b32 -mac_gl_load_functions(){ - b32 result = true; +@interface OpenGLView : NSOpenGLView +- (void)initGL; +- (void)render:(Render_Target*)target; +@end + +global OpenGLView *opengl_view; + +@implementation OpenGLView{ + b32 glIsInitialized; +} + +- (id)init{ + self = [super init]; + if (self == nil){ + return nil; + } - // NOTE(yuval): Open the gl dynamic library - void* gl_image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY); + glIsInitialized = false; + [self initGL]; + + return self; +} + +- (void)dealloc{ + [super dealloc]; +} + +- (void)prepareOpenGL{ + [super prepareOpenGL]; + + [[self openGLContext] makeCurrentContext]; + + // NOTE(yuval): Setup vsync + GLint swapInt = 1; + printf("Using vsync: %d\n", swapInt); + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; +} + +- (void)awakeFromNib{ + [self initGL]; +} + +- (void)reshape{ + [super reshape]; + + [[self openGLContext] makeCurrentContext]; + [[self openGLContext] update]; +} + +- (void)initGL{ + if (glIsInitialized){ + return; + } + + // NOTE(yuval): Setup OpenGL + NSOpenGLPixelFormatAttribute opengl_attrs[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFASampleBuffers, 1, + NSOpenGLPFASamples, 16, + 0 + }; + + NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:opengl_attrs]; + if (pixel_format == nil){ + fprintf(stderr, "Error creating OpenGLPixelFormat\n"); + exit(1); + } + + NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:nil]; + + [self setPixelFormat:pixel_format]; + [self setOpenGLContext:context]; + + [context makeCurrentContext]; + + [pixel_format release]; + + glIsInitialized = true; +} + +- (void)render:(Render_Target*)target{ + Assert(glIsInitialized); + + CGLLockContext([[self openGLContext] CGLContextObj]); + [[self openGLContext] makeCurrentContext]; + gl_render(target); + [[self openGLContext] flushBuffer]; + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} +@end + +function void +mac_gl_init(NSWindow *window){ + // NOTE(yuval): Create OpenGLView + NSView *content_view = [window contentView]; + + opengl_view = [[OpenGLView alloc] init]; + [opengl_view setFrame:[content_view bounds]]; + [opengl_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [opengl_view setWantsBestResolutionOpenGLSurface:YES]; + + // NOTE(yuval): Add the OpenGL view as a subview of the window + [content_view addSubview:opengl_view]; // NOTE(yuval): Load gl functions -#define GL_FUNC(f,R,P) Stmnt((f) = (f##_Function*)dlsym(gl_image, #f); \ - (result) &= (f != 0);); -#include "mac_4ed_opengl_funcs.h" + void *gl_image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY); - return result; +#define GL_FUNC(f,R,P) ((f) = (f##_Function*)dlsym(gl_image, #f)); +#include "mac_4ed_opengl_funcs.h" } + +function void +mac_gl_render(Render_Target* target){ + f64 begin_time = system_now_time() / 1000000.0; + [opengl_view render:target]; + f64 end_time = system_now_time() / 1000000.0; + printf("Render Time: %fs\n", (end_time - begin_time)); +} \ No newline at end of file From 0fceec19a9cf4ffa988c24c356b04b8410c6e0f7 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sat, 4 Jan 2020 03:24:52 +0200 Subject: [PATCH 41/67] Basic metal renderer (only rendering a triangle as of right now). --- bin/4ed_build.cpp | 4 +- metal/4ed_metal_render.mm | 136 +++++++++++++++++++++++++++++++++ metal/AAPLShaderTypes.h | 33 ++++++++ metal/AAPLShaders.metal | 64 ++++++++++++++++ opengl/4ed_opengl_render.cpp | 12 +++ platform_mac/mac_4ed.mm | 33 ++++++-- platform_mac/mac_4ed_metal.mm | 29 +++++++ platform_mac/mac_4ed_opengl.mm | 34 +++++++-- 8 files changed, 332 insertions(+), 13 deletions(-) create mode 100644 metal/4ed_metal_render.mm create mode 100644 metal/AAPLShaderTypes.h create mode 100644 metal/AAPLShaders.metal create mode 100644 platform_mac/mac_4ed_metal.mm diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 03a9a1b2..0a98abdb 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -389,7 +389,7 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha #define CLANG_LIBS_COMMON \ "-framework Cocoa -framework QuartzCore " \ "-framework CoreServices " \ -"-framework OpenGL -framework IOKit " +"-framework OpenGL -framework IOKit -framework Metal -framework MetalKit " #define CLANG_LIBS_X64 CLANG_LIBS_COMMON \ FOREIGN "/x64/libfreetype-mac.a" @@ -398,7 +398,7 @@ FOREIGN "/x64/libfreetype-mac.a" FOREIGN "/x86/libfreetype-mac.a" #else -# error gcc options not set for this platform +# error clang options not set for this platform #endif internal void diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm new file mode 100644 index 00000000..e715a8fe --- /dev/null +++ b/metal/4ed_metal_render.mm @@ -0,0 +1,136 @@ +/* 4coder Metal render implementation */ + +#undef clamp +#undef function +#import +#import + +// Header shared between C code here, which executes Metal API commands, and .metal files, which +// uses these types as inputs to the shaders. +#import "AAPLShaderTypes.h" + +#define function static +#define clamp(a,x,b) clamp_((a),(x),(b)) + +@interface FCoderMetalRenderer : NSObject +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView; +@end + +@implementation FCoderMetalRenderer{ + id _device; + + // The render pipeline generated from the vertex and fragment shaders in the .metal shader file. + id _pipelineState; + + // The command queue used to pass commands to the device. + id _commandQueue; + + // The current size of the view, used as an input to the vertex shader. + vector_uint2 _viewportSize; +} + +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView{ + self = [super init]; + if(self) + { + NSError *error = nil; + + _device = mtkView.device; + + // Load all the shader files with a .metal file extension in the project. + id defaultLibrary = [_device newLibraryWithFile:@"shaders/AAPLShaders.metallib" + error:&error]; + Assert(error == nil); + + id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; + id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; + + // Configure a pipeline descriptor that is used to create a pipeline state. + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipelineStateDescriptor.label = @"Simple Pipeline"; + pipelineStateDescriptor.vertexFunction = vertexFunction; + pipelineStateDescriptor.fragmentFunction = fragmentFunction; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat; + + _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor + error:&error]; + + // Pipeline State creation could fail if the pipeline descriptor isn't set up properly. + // If the Metal API validation is enabled, you can find out more information about what + // went wrong. (Metal API validation is enabled by default when a debug build is run + // from Xcode.) + NSAssert(_pipelineState, @"Failed to created pipeline state: %@", error); + + // Create the command queue + _commandQueue = [_device newCommandQueue]; + + u32 max_buffer_size = (u32)[_device maxBufferLength]; + printf("Max Buffer Size: %u - Which is %lu vertices\n", max_buffer_size, (max_buffer_size / sizeof(Render_Vertex))); + } + + return self; +} + +/// Called whenever view changes orientation or is resized +- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size{ + // Save the size of the drawable to pass to the vertex shader. + +} + +/// Called whenever the view needs to render a frame. +- (void)drawInMTKView:(nonnull MTKView *)view{ + CGSize size = [view drawableSize]; + _viewportSize.x = size.width; + _viewportSize.y = size.height; + + static const AAPLVertex triangleVertices[] = + { + // 2D positions, RGBA colors + { { 250, -250 }, { 1, 0, 0, 1 } }, + { { -250, -250 }, { 0, 1, 0, 1 } }, + { { 0, 250 }, { 0, 0, 1, 1 } }, + }; + + // Create a new command buffer for each render pass to the current drawable. + id commandBuffer = [_commandQueue commandBuffer]; + commandBuffer.label = @"MyCommand"; + + // Obtain a renderPassDescriptor generated from the view's drawable textures. + MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; + + if(renderPassDescriptor != nil) + { + // Create a render command encoder. + id renderEncoder = + [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + renderEncoder.label = @"MyRenderEncoder"; + + // Set the region of the drawable to draw into. + [renderEncoder setViewport:(MTLViewport){0.0, 0.0, (double)_viewportSize.x, (double)_viewportSize.y, 0.0, 1.0 }]; + + [renderEncoder setRenderPipelineState:_pipelineState]; + + // Pass in the parameter data. + [renderEncoder setVertexBytes:triangleVertices + length:sizeof(triangleVertices) + atIndex:AAPLVertexInputIndexVertices]; + + [renderEncoder setVertexBytes:&_viewportSize + length:sizeof(_viewportSize) + atIndex:AAPLVertexInputIndexViewportSize]; + + // Draw the triangle. + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:3]; + + [renderEncoder endEncoding]; + + // Schedule a present once the framebuffer is complete using the current drawable. + [commandBuffer presentDrawable:view.currentDrawable]; + } + + // Finalize rendering here & push the command buffer to the GPU. + [commandBuffer commit]; +} +@end \ No newline at end of file diff --git a/metal/AAPLShaderTypes.h b/metal/AAPLShaderTypes.h new file mode 100644 index 00000000..be46a81a --- /dev/null +++ b/metal/AAPLShaderTypes.h @@ -0,0 +1,33 @@ +/* +See LICENSE folder for this sample’s licensing information. + +Abstract: +Header containing types and enum constants shared between Metal shaders and C/ObjC source +*/ + +#ifndef AAPLShaderTypes_h +#define AAPLShaderTypes_h + +#undef clamp +#include +#define clamp(a,x,b) clamp_((a),(x),(b)) + +// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs +// match Metal API buffer set calls. +typedef enum AAPLVertexInputIndex +{ + AAPLVertexInputIndexVertices = 0, + AAPLVertexInputIndexViewportSize = 1, +} AAPLVertexInputIndex; + +// This structure defines the layout of vertices sent to the vertex +// shader. This header is shared between the .metal shader and C code, to guarantee that +// the layout of the vertex array in the C code matches the layout that the .metal +// vertex shader expects. +typedef struct +{ + vector_float2 position; + vector_float4 color; +} AAPLVertex; + +#endif /* AAPLShaderTypes_h */ diff --git a/metal/AAPLShaders.metal b/metal/AAPLShaders.metal new file mode 100644 index 00000000..60c3736b --- /dev/null +++ b/metal/AAPLShaders.metal @@ -0,0 +1,64 @@ +/* +See LICENSE folder for this sample’s licensing information. + +Abstract: +Metal shaders used for this sample +*/ + +#include +#include + +using namespace metal; + +// Include header shared between this Metal shader code and C code executing Metal API commands. +#import "AAPLShaderTypes.h" + +// Vertex shader outputs and fragment shader inputs +typedef struct +{ + // The [[position]] attribute of this member indicates that this value + // is the clip space position of the vertex when this structure is + // returned from the vertex function. + float4 position [[position]]; + + // Since this member does not have a special attribute, the rasterizer + // interpolates its value with the values of the other triangle vertices + // and then passes the interpolated value to the fragment shader for each + // fragment in the triangle. + float4 color; + +} RasterizerData; + +vertex RasterizerData +vertexShader(uint vertexID [[vertex_id]], + constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], + constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]]) +{ + RasterizerData out; + + // Index into the array of positions to get the current vertex. + // The positions are specified in pixel dimensions (i.e. a value of 100 + // is 100 pixels from the origin). + float2 pixelSpacePosition = vertices[vertexID].position.xy; + + // Get the viewport size and cast to float. + vector_float2 viewportSize = vector_float2(*viewportSizePointer); + + + // To convert from positions in pixel space to positions in clip-space, + // divide the pixel coordinates by half the size of the viewport. + out.position = vector_float4(0.0, 0.0, 0.0, 1.0); + out.position.xy = pixelSpacePosition / (viewportSize / 2.0); + + // Pass the input color directly to the rasterizer. + out.color = vertices[vertexID].color; + + return out; +} + +fragment float4 fragmentShader(RasterizerData in [[stage_in]]) +{ + // Return the interpolated color. + return in.color; +} + diff --git a/opengl/4ed_opengl_render.cpp b/opengl/4ed_opengl_render.cpp index e3413ae0..7c2d5524 100644 --- a/opengl/4ed_opengl_render.cpp +++ b/opengl/4ed_opengl_render.cpp @@ -285,6 +285,9 @@ gl_render(Render_Target *t){ t->free_texture_first = 0; t->free_texture_last = 0; + u32 all_vertex_count = 0; + + u64 begin_draw = system_now_time(); for (Render_Group *group = t->group_first; group != 0; group = group->next){ @@ -339,9 +342,18 @@ gl_render(Render_Target *t){ glDisableVertexAttribArray(gpu_program.vertex_c); glDisableVertexAttribArray(gpu_program.vertex_ht); } + + all_vertex_count += vertex_count; } + u64 end_draw = system_now_time(); + printf("Draw time: %fs\n", mac_get_time_diff_sec(begin_draw, end_draw)); + u64 begin_flush = system_now_time(); glFlush(); + u64 end_flush = system_now_time(); + printf("Flush time: %fs\n", mac_get_time_diff_sec(begin_flush, end_flush)); + + printf("Drawn %d Vertices\n", all_vertex_count); } // BOTTOM diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 958d1399..cae3a944 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -273,11 +273,24 @@ mac_to_object(Plat_Handle handle){ #include #import "mac_4ed_opengl.mm" +#import "mac_4ed_metal.mm" + #include "4ed_font_provider_freetype.h" #include "4ed_font_provider_freetype.cpp" #import "mac_4ed_functions.mm" + + +//////////////////////////////// + +global Key_Code keycode_lookup_table[255]; + +function void +mac_key_code_init(void){ + +} + //////////////////////////////// function void @@ -326,8 +339,6 @@ mac_resize(NSWindow *window){ //////////////////////////////// -// TODO(yuval): mac_resize(bounds.size.width, bounds.size.height); - @implementation FCoderAppDelegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -376,6 +387,13 @@ mac_resize(NSWindow *window){ } - (void)drawRect:(NSRect)bounds{ + /* NOTE(yuval): Force the graphics context to clear to black so we don't +get a flash of white until the app is ready to draw. In practice on modern macOS, +this only gets called for window creation and other extraordinary events. +(Taken From SDL) */ + [[NSColor blackColor] setFill]; + NSRectFill(bounds); + // NOTE(yuval): Read comment in win32_4ed.cpp's main loop system_mutex_release(mac_vars.global_frame_mutex); @@ -422,7 +440,8 @@ mac_resize(NSWindow *window){ [NSApp terminate:nil]; } - mac_gl_render(&target); + // mac_gl_render(&target); + mac_metal_render(&target); mac_vars.first = false; @@ -670,17 +689,19 @@ main(int arg_count, char **args){ NSView* content_view = [mac_vars.window contentView]; - // NOTE(yuval): Initialize the renderer - mac_gl_init(mac_vars.window); - // NOTE(yuval): Create the 4coder view mac_vars.view = [[FCoderView alloc] init]; [mac_vars.view setFrame:[content_view bounds]]; [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + // NOTE(yuval): Display window and view [content_view addSubview:mac_vars.view]; [mac_vars.window makeKeyAndOrderFront:nil]; + // NOTE(yuval): Initialize the renderer + mac_gl_init(mac_vars.window); + mac_metal_init(mac_vars.window); + mac_resize(w, h); // diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm new file mode 100644 index 00000000..05ed482e --- /dev/null +++ b/platform_mac/mac_4ed_metal.mm @@ -0,0 +1,29 @@ +#import "metal/4ed_metal_render.mm" + +global MTKView *metal_view; +global FCoderMetalRenderer *metal_renderer; + +function void +mac_metal_init(NSWindow *window){ + // NOTE(yuval): Create Metal view + NSView *content_view = [window contentView]; + + metal_view = [[MTKView alloc] initWithFrame:[content_view bounds]]; + [metal_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + metal_view.device = MTLCreateSystemDefaultDevice(); + + // NOTE(yuval): Add the Metal view as a subview of the window + [content_view addSubview:metal_view]; + + // NOTE(yuval): Create the Metal renderer + metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; +} + +function void +mac_metal_render(Render_Target* target){ + u64 begin_time = system_now_time(); + [metal_renderer drawInMTKView:metal_view]; + u64 end_time = system_now_time(); + printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); +} \ No newline at end of file diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index dd89c079..ba1f4dee 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -5,6 +5,11 @@ #define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0; #include "mac_4ed_opengl_funcs.h" +f64 mac_get_time_diff_sec(u64 begin, u64 end){ + f64 result = ((end - begin) / 1000000.0); + return result; +} + #include "opengl/4ed_opengl_render.cpp" @interface OpenGLView : NSOpenGLView @@ -12,8 +17,6 @@ - (void)render:(Render_Target*)target; @end -global OpenGLView *opengl_view; - @implementation OpenGLView{ b32 glIsInitialized; } @@ -95,14 +98,35 @@ global OpenGLView *opengl_view; - (void)render:(Render_Target*)target{ Assert(glIsInitialized); + u64 context_lock_begin = system_now_time(); CGLLockContext([[self openGLContext] CGLContextObj]); + u64 context_lock_end = system_now_time(); + printf("Context lock time: %fs\n", mac_get_time_diff_sec(context_lock_begin, context_lock_end)); + + u64 make_current_context_begin = system_now_time(); [[self openGLContext] makeCurrentContext]; + u64 make_current_context_end = system_now_time(); + printf("Make current context time: %fs\n", mac_get_time_diff_sec(make_current_context_begin, make_current_context_end)); + + u64 gl_render_begin = system_now_time(); gl_render(target); + u64 gl_render_end = system_now_time(); + printf("GL render time: %fs\n", mac_get_time_diff_sec(gl_render_begin, gl_render_end)); + + u64 gl_flush_buffer_begin = system_now_time(); [[self openGLContext] flushBuffer]; + u64 gl_flush_buffer_end = system_now_time(); + printf("GL flush buffer time: %fs\n", mac_get_time_diff_sec(gl_flush_buffer_begin, gl_flush_buffer_end)); + + u64 context_unlock_begin = system_now_time(); CGLUnlockContext([[self openGLContext] CGLContextObj]); + u64 context_unlock_end = system_now_time(); + printf("Context unlock time: %fs\n", mac_get_time_diff_sec(context_unlock_begin, context_unlock_end)); } @end +global OpenGLView *opengl_view; + function void mac_gl_init(NSWindow *window){ // NOTE(yuval): Create OpenGLView @@ -125,8 +149,8 @@ mac_gl_init(NSWindow *window){ function void mac_gl_render(Render_Target* target){ - f64 begin_time = system_now_time() / 1000000.0; + u64 begin_time = system_now_time(); [opengl_view render:target]; - f64 end_time = system_now_time() / 1000000.0; - printf("Render Time: %fs\n", (end_time - begin_time)); + u64 end_time = system_now_time(); + printf("Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); } \ No newline at end of file From b52f1cee246a37ed5850df7cc08af3230237e20f Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 5 Jan 2020 03:13:47 +0200 Subject: [PATCH 42/67] Metal projection matrix test. --- bin/4ed_build.cpp | 11 +- metal/4ed_metal_render.mm | 245 +++++++++++++++++++++++++++++++++- platform_mac/mac_4ed_metal.mm | 8 +- 3 files changed, 250 insertions(+), 14 deletions(-) diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 0a98abdb..8640443b 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -380,11 +380,12 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha #if OS_MAC -# define CLANG_OPTS \ -"-Wno-write-strings -Wno-deprecated-declarations " \ -"-Wno-comment -Wno-switch -Wno-null-dereference " \ -"-Wno-tautological-compare " \ -"-Wno-unused-result -Wno-missing-declarations -std=c++11 " +# define CLANG_OPTS \ +"-Wno-write-strings -Wno-deprecated-declarations " \ +"-Wno-comment -Wno-switch -Wno-null-dereference " \ +"-Wno-tautological-compare -Wno-unused-result " \ +"-Wno-missing-declarations -Wno-nullability-completeness " \ +"-std=c++11 " #define CLANG_LIBS_COMMON \ "-framework Cocoa -framework QuartzCore " \ diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index e715a8fe..442a3e50 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -5,12 +5,88 @@ #import #import -// Header shared between C code here, which executes Metal API commands, and .metal files, which -// uses these types as inputs to the shaders. -#import "AAPLShaderTypes.h" - +#include "AAPLShaderTypes.h" #define function static -#define clamp(a,x,b) clamp_((a),(x),(b)) + +struct Metal_Renderer{ + MTKView *view; + + id device; + id pipeline_state; + id command_queue; + id buffer; +}; + +global_const u32 metal_max_vertices = (1<<16); + +global_const char *metal__shaders_source = R"( +#include +#include + +using namespace metal; + +// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs +// match Metal API buffer set calls. +typedef enum AAPLVertexInputIndex +{ + AAPLVertexInputIndexVertices = 0, + AAPLVertexInputIndexViewportSize = 1, +} AAPLVertexInputIndex; + +// This structure defines the layout of vertices sent to the vertex +// shader. This header is shared between the .metal shader and C code, to guarantee that +// the layout of the vertex array in the C code matches the layout that the .metal +// vertex shader expects. +typedef struct +{ + vector_float2 position; + vector_float4 color; +} AAPLVertex; + +// Vertex shader outputs and fragment shader inputs +typedef struct +{ + // The [[position]] attribute of this member indicates that this value + // is the clip space position of the vertex when this structure is + // returned from the vertex function. + float4 position [[position]]; + + // Since this member does not have a special attribute, the rasterizer + // interpolates its value with the values of the other triangle vertices + // and then passes the interpolated value to the fragment shader for each + // fragment in the triangle. + float4 color; + +} RasterizerData; + +vertex RasterizerData +vertexShader(uint vertexID [[vertex_id]], + constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], + constant float4x4 &projMatrix[[buffer(AAPLVertexInputIndexViewportSize)]]) +{ + RasterizerData out; + + // Index into the array of positions to get the current vertex. + // The positions are specified in pixel dimensions (i.e. a value of 100 + // is 100 pixels from the origin). + float2 pixelSpacePosition = vertices[vertexID].position.xy; + + // To convert from positions in pixel space to positions in clip-space, + // divide the pixel coordinates by half the size of the viewport. + out.position = float4(pixelSpacePosition, 0.0, 1.0) * projMatrix; + + // Pass the input color directly to the rasterizer. + out.color = vertices[vertexID].color; + + return out; +} + +fragment float4 fragmentShader(RasterizerData in [[stage_in]]) +{ + // Return the interpolated color. + return in.color; +} +)"; @interface FCoderMetalRenderer : NSObject - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView; @@ -133,4 +209,161 @@ // Finalize rendering here & push the command buffer to the GPU. [commandBuffer commit]; } -@end \ No newline at end of file +@end + +function b32 +metal_init(Metal_Renderer *renderer, MTKView *view){ + NSError *error = nil; + + renderer->view = view; + renderer->device = view.device; + + // NOTE(yuval): Compile the shaders + id vertex_function = nil; + id fragment_function = nil; + { + NSString *shaders_source_str = [NSString stringWithUTF8String:metal__shaders_source]; + + MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; + options.fastMathEnabled = YES; + + id shader_library = [renderer->device newLibraryWithSource:shaders_source_str + options:options error:&error]; + vertex_function = [shader_library newFunctionWithName:@"vertexShader"]; + fragment_function = [shader_library newFunctionWithName:@"fragmentShader"]; + + [options release]; + } + + if (error != nil){ + return(false); + } + + // NOTE(yuval): Configure the pipeline descriptor + { + MTLRenderPipelineDescriptor *pipeline_state_descriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipeline_state_descriptor.label = @"4coder Metal Renderer Pipeline"; + pipeline_state_descriptor.vertexFunction = vertex_function; + pipeline_state_descriptor.fragmentFunction = fragment_function; + pipeline_state_descriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; + + renderer->pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor + error:&error]; + } + + if (error != nil){ + return(false); + } + + // NOTE(yuval): Create the command queue + renderer->command_queue = [renderer->device newCommandQueue]; + + // NOTE(yuval): Create the vertex buffer + { + u32 buffer_size = (metal_max_vertices * sizeof(Render_Vertex)); + MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; + renderer->buffer = [renderer->device newBufferWithLength:buffer_size + options:options]; + } + + return(true); +} + +function void +metal_render(Metal_Renderer *renderer, Render_Target *t){ + static const AAPLVertex triangleVertices[] = { + // 2D positions, RGBA colors + { { 200, 100 }, { 1, 0, 0, 1 } }, + { { 100, 100 }, { 0, 1, 0, 1 } }, + { { 150, 200 }, { 0, 0, 1, 1 } }, + }; + + // NOTE(yuval): Create the command buffer + id command_buffer = [renderer->command_queue commandBuffer]; + command_buffer.label = @"4coder Metal Render Command"; + + // NOTE(yuval): Obtain the render pass descriptor from the renderer's view + MTLRenderPassDescriptor *render_pass_descriptor = renderer->view.currentRenderPassDescriptor; + if (render_pass_descriptor != nil){ + // NOTE(yuval): Create the render command encoder + id render_encoder + = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + render_encoder.label = @"4coder Render Encoder"; + + // NOTE(yuval): Set the region of the drawable to draw into + [render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)t->width, (double)t->height, 0.0, 1.0}]; + + // NOTE(yuval): Set the render pipeline to use for drawing + [render_encoder setRenderPipelineState:renderer->pipeline_state]; + + // NOTE(yuval): Pass in the parameter data + [render_encoder setVertexBytes:triangleVertices + length:sizeof(triangleVertices) + atIndex:AAPLVertexInputIndexVertices]; + +#if 0 + vector_uint2 viewport_size = {(u32)t->width, (u32)t->height}; + [render_encoder setVertexBytes:&viewport_size + length:sizeof(viewport_size) + atIndex:AAPLVertexInputIndexViewportSize]; +#else + float left = 0, right = (float)t->width; + float bottom = 0, top = (float)t->height; + float near_depth = -1.0f, far_depth = 1.0f; + float m[16] = { + 2.0f / (right - left), 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f / (top - bottom), 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f / (far_depth - near_depth), 0.0f, + -((right + left) / (right - left)), -((top + bottom) / (top - bottom)), + (-near_depth) * (far_depth - near_depth), 1.0f + }; + + float sLength = 1.0f / (right - left); + float sHeight = 1.0f / (top - bottom); + float sDepth = 1.0f / (far_depth - near_depth); + + simd::float4 P; + simd::float4 Q; + simd::float4 R; + simd::float4 S; + + P.x = 2.0f * sLength; + P.y = 0.0f; + P.z = 0.0f; + P.w = -((right + left) / (right - left)); + + Q.x = 0.0f; + Q.y = 2.0f * sHeight; + Q.z = 0.0f; + Q.w = -((top + bottom) / (top - bottom)); + + R.x = 0.0f; + R.y = 0.0f; + R.z = sDepth; + R.w = -near_depth * sDepth; + + S.x = 0.0f; + S.y = 0.0f; + S.z = 0.0f; + S.w = 1.0f; + + simd_float4x4 proj = simd::float4x4(P, Q, R, S); + + [render_encoder setVertexBytes:&proj + length:sizeof(proj) + atIndex:AAPLVertexInputIndexViewportSize]; +#endif + + // NOTE(yuval): Draw the triangle + [render_encoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:3]; + + [render_encoder endEncoding]; + + // NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable + [command_buffer presentDrawable:renderer->view.currentDrawable]; + } + + [command_buffer commit]; +} \ No newline at end of file diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 05ed482e..ff69d8d3 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -1,7 +1,7 @@ #import "metal/4ed_metal_render.mm" +global Metal_Renderer metal_renderer; global MTKView *metal_view; -global FCoderMetalRenderer *metal_renderer; function void mac_metal_init(NSWindow *window){ @@ -17,13 +17,15 @@ mac_metal_init(NSWindow *window){ [content_view addSubview:metal_view]; // NOTE(yuval): Create the Metal renderer - metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; + //metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; + metal_init(&metal_renderer, metal_view); } function void mac_metal_render(Render_Target* target){ u64 begin_time = system_now_time(); - [metal_renderer drawInMTKView:metal_view]; + //[metal_renderer drawInMTKView:metal_view]; + metal_render(&metal_renderer, target); u64 end_time = system_now_time(); printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); } \ No newline at end of file From 813ba593e3fe263c79139fc8fe4079e2570155be Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 5 Jan 2020 03:44:16 +0200 Subject: [PATCH 43/67] Fixed flipped projection matrix. --- metal/4ed_metal_render.mm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 442a3e50..99a6de2f 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -73,7 +73,7 @@ vertexShader(uint vertexID [[vertex_id]], // To convert from positions in pixel space to positions in clip-space, // divide the pixel coordinates by half the size of the viewport. - out.position = float4(pixelSpacePosition, 0.0, 1.0) * projMatrix; + out.position = projMatrix * float4(pixelSpacePosition, 0.0, 1.0); // Pass the input color directly to the rasterizer. out.color = vertices[vertexID].color; @@ -315,7 +315,7 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ 0.0f, 2.0f / (top - bottom), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f / (far_depth - near_depth), 0.0f, -((right + left) / (right - left)), -((top + bottom) / (top - bottom)), - (-near_depth) * (far_depth - near_depth), 1.0f + (-near_depth) / (far_depth - near_depth), 1.0f }; float sLength = 1.0f / (right - left); @@ -330,21 +330,21 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ P.x = 2.0f * sLength; P.y = 0.0f; P.z = 0.0f; - P.w = -((right + left) / (right - left)); + P.w = 0.0f; Q.x = 0.0f; Q.y = 2.0f * sHeight; Q.z = 0.0f; - Q.w = -((top + bottom) / (top - bottom)); + Q.w = 0.0f; R.x = 0.0f; R.y = 0.0f; R.z = sDepth; - R.w = -near_depth * sDepth; + R.w = 0.0f; - S.x = 0.0f; - S.y = 0.0f; - S.z = 0.0f; + S.x = -((right + left) / (right - left)); + S.y = -((top + bottom) / (top - bottom)); + S.z = -near_depth * sDepth; S.w = 1.0f; simd_float4x4 proj = simd::float4x4(P, Q, R, S); From efad77240152051750eb6f60272916b76d383c64 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 6 Jan 2020 02:55:07 +0200 Subject: [PATCH 44/67] Work on metal renderer. Only semi-working for now. --- metal/4ed_metal_render.mm | 414 ++++++++++++++--------------------- metal/AAPLShaderTypes.h | 8 - opengl/4ed_opengl_render.cpp | 2 +- platform_mac/mac_4ed.mm | 2 +- 4 files changed, 171 insertions(+), 255 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 99a6de2f..cf760786 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -15,6 +15,7 @@ struct Metal_Renderer{ id pipeline_state; id command_queue; id buffer; + id capture_scope; }; global_const u32 metal_max_vertices = (1<<16); @@ -25,193 +26,94 @@ global_const char *metal__shaders_source = R"( using namespace metal; -// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs -// match Metal API buffer set calls. -typedef enum AAPLVertexInputIndex -{ - AAPLVertexInputIndexVertices = 0, - AAPLVertexInputIndexViewportSize = 1, -} AAPLVertexInputIndex; +//////////////////////////////// -// This structure defines the layout of vertices sent to the vertex -// shader. This header is shared between the .metal shader and C code, to guarantee that -// the layout of the vertex array in the C code matches the layout that the .metal -// vertex shader expects. -typedef struct -{ - vector_float2 position; - vector_float4 color; -} AAPLVertex; +typedef struct{ +packed_float2 xy; + packed_float3 uvw; +uint32_t color; +float half_thickness; +} Vertex; -// Vertex shader outputs and fragment shader inputs -typedef struct -{ - // The [[position]] attribute of this member indicates that this value - // is the clip space position of the vertex when this structure is - // returned from the vertex function. - float4 position [[position]]; - - // Since this member does not have a special attribute, the rasterizer - // interpolates its value with the values of the other triangle vertices - // and then passes the interpolated value to the fragment shader for each - // fragment in the triangle. +// NOTE(yuval): Vertex shader outputs and fragment shader inputs +typedef struct{ +// NOTE(yuval): Vertex shader output +float4 position [[position]]; + +// NOTE(yuval): Fragment shader inputs float4 color; - -} RasterizerData; + float3 uvw; + float2 xy; + float2 adjusted_half_dim; + float half_thickness; +} Rasterizer_Data; -vertex RasterizerData -vertexShader(uint vertexID [[vertex_id]], - constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], - constant float4x4 &projMatrix[[buffer(AAPLVertexInputIndexViewportSize)]]) -{ - RasterizerData out; +//////////////////////////////// + +vertex Rasterizer_Data +vertex_shader(uint vertex_id [[vertex_id]], constant Vertex *vertices [[buffer(0)]], +constant float4x4 &proj [[buffer(1)]]){ + constant Vertex *in = &vertices[vertex_id]; + Rasterizer_Data out; - // Index into the array of positions to get the current vertex. - // The positions are specified in pixel dimensions (i.e. a value of 100 - // is 100 pixels from the origin). - float2 pixelSpacePosition = vertices[vertexID].position.xy; + // NOTE(yuval): Calculate position in NDC + out.position = proj * float4(in->xy, 0.0, 1.0); - // To convert from positions in pixel space to positions in clip-space, - // divide the pixel coordinates by half the size of the viewport. - out.position = projMatrix * float4(pixelSpacePosition, 0.0, 1.0); + // NOTE(yuval): Convert color to float4 format + out.color.b = ((float((in->color ) & 0xFFu)) / 255.0); + out.color.g = ((float((in->color >> 8u) & 0xFFu)) / 255.0); + out.color.r = ((float((in->color >> 16u) & 0xFFu)) / 255.0); + out.color.a = ((float((in->color >> 24u) & 0xFFu)) / 255.0); - // Pass the input color directly to the rasterizer. - out.color = vertices[vertexID].color; + // NOTE(yuval): Pass uvw coordinates to the fragment shader + out.uvw = in->uvw; - return out; + // NOTE(yuval): Calculate adjusted half dim + float2 center = in->uvw.xy; + float2 half_dim = abs(in->xy - center); + out.adjusted_half_dim = (half_dim - in->uvw.zz + float2(0.5, 0.5)); + + // NOTE(yuval): Pass half_thickness to the fragment shader + out.half_thickness = in->half_thickness; + + // NOTE(yuval): Pass xy to the fragment shader + out.xy = in->xy; + + return(out); } -fragment float4 fragmentShader(RasterizerData in [[stage_in]]) -{ - // Return the interpolated color. - return in.color; +//////////////////////////////// + +float +rectangle_sd(float2 p, float2 b){ +float2 d = (abs(p) - b); +float result = (length(max(d, float2(0.0, 0.0))) + min(max(d.x, d.y), 0.0)); + +return(result); +} + +fragment float4 +fragment_shader(Rasterizer_Data in [[stage_in]]){ +float has_thickness = step(0.49, in.half_thickness); +// float does_not_have_thickness = (1.0 - has_thickness); + +// TODO(yuval): Sample texture here. + +float2 center = in.uvw.xy; +float roundness = in.uvw.z; +float sd = rectangle_sd(in.xy - center, in.adjusted_half_dim); +sd = sd - roundness; +sd = (abs(sd + in.half_thickness) - in.half_thickness); +float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd)); +shape_value *= has_thickness; + + // TOOD(yuval): Add sample_value to alpha + float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value)); + return(out_color); } )"; -@interface FCoderMetalRenderer : NSObject -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView; -@end - -@implementation FCoderMetalRenderer{ - id _device; - - // The render pipeline generated from the vertex and fragment shaders in the .metal shader file. - id _pipelineState; - - // The command queue used to pass commands to the device. - id _commandQueue; - - // The current size of the view, used as an input to the vertex shader. - vector_uint2 _viewportSize; -} - -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView{ - self = [super init]; - if(self) - { - NSError *error = nil; - - _device = mtkView.device; - - // Load all the shader files with a .metal file extension in the project. - id defaultLibrary = [_device newLibraryWithFile:@"shaders/AAPLShaders.metallib" - error:&error]; - Assert(error == nil); - - id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; - id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; - - // Configure a pipeline descriptor that is used to create a pipeline state. - MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; - pipelineStateDescriptor.label = @"Simple Pipeline"; - pipelineStateDescriptor.vertexFunction = vertexFunction; - pipelineStateDescriptor.fragmentFunction = fragmentFunction; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat; - - _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor - error:&error]; - - // Pipeline State creation could fail if the pipeline descriptor isn't set up properly. - // If the Metal API validation is enabled, you can find out more information about what - // went wrong. (Metal API validation is enabled by default when a debug build is run - // from Xcode.) - NSAssert(_pipelineState, @"Failed to created pipeline state: %@", error); - - // Create the command queue - _commandQueue = [_device newCommandQueue]; - - u32 max_buffer_size = (u32)[_device maxBufferLength]; - printf("Max Buffer Size: %u - Which is %lu vertices\n", max_buffer_size, (max_buffer_size / sizeof(Render_Vertex))); - } - - return self; -} - -/// Called whenever view changes orientation or is resized -- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size{ - // Save the size of the drawable to pass to the vertex shader. - -} - -/// Called whenever the view needs to render a frame. -- (void)drawInMTKView:(nonnull MTKView *)view{ - CGSize size = [view drawableSize]; - _viewportSize.x = size.width; - _viewportSize.y = size.height; - - static const AAPLVertex triangleVertices[] = - { - // 2D positions, RGBA colors - { { 250, -250 }, { 1, 0, 0, 1 } }, - { { -250, -250 }, { 0, 1, 0, 1 } }, - { { 0, 250 }, { 0, 0, 1, 1 } }, - }; - - // Create a new command buffer for each render pass to the current drawable. - id commandBuffer = [_commandQueue commandBuffer]; - commandBuffer.label = @"MyCommand"; - - // Obtain a renderPassDescriptor generated from the view's drawable textures. - MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; - - if(renderPassDescriptor != nil) - { - // Create a render command encoder. - id renderEncoder = - [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - renderEncoder.label = @"MyRenderEncoder"; - - // Set the region of the drawable to draw into. - [renderEncoder setViewport:(MTLViewport){0.0, 0.0, (double)_viewportSize.x, (double)_viewportSize.y, 0.0, 1.0 }]; - - [renderEncoder setRenderPipelineState:_pipelineState]; - - // Pass in the parameter data. - [renderEncoder setVertexBytes:triangleVertices - length:sizeof(triangleVertices) - atIndex:AAPLVertexInputIndexVertices]; - - [renderEncoder setVertexBytes:&_viewportSize - length:sizeof(_viewportSize) - atIndex:AAPLVertexInputIndexViewportSize]; - - // Draw the triangle. - [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle - vertexStart:0 - vertexCount:3]; - - [renderEncoder endEncoding]; - - // Schedule a present once the framebuffer is complete using the current drawable. - [commandBuffer presentDrawable:view.currentDrawable]; - } - - // Finalize rendering here & push the command buffer to the GPU. - [commandBuffer commit]; -} -@end - -function b32 +function void metal_init(Metal_Renderer *renderer, MTKView *view){ NSError *error = nil; @@ -229,15 +131,13 @@ metal_init(Metal_Renderer *renderer, MTKView *view){ id shader_library = [renderer->device newLibraryWithSource:shaders_source_str options:options error:&error]; - vertex_function = [shader_library newFunctionWithName:@"vertexShader"]; - fragment_function = [shader_library newFunctionWithName:@"fragmentShader"]; + vertex_function = [shader_library newFunctionWithName:@"vertex_shader"]; + fragment_function = [shader_library newFunctionWithName:@"fragment_shader"]; [options release]; } - if (error != nil){ - return(false); - } + Assert(error == nil); // NOTE(yuval): Configure the pipeline descriptor { @@ -246,14 +146,17 @@ metal_init(Metal_Renderer *renderer, MTKView *view){ pipeline_state_descriptor.vertexFunction = vertex_function; pipeline_state_descriptor.fragmentFunction = fragment_function; pipeline_state_descriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; + pipeline_state_descriptor.colorAttachments[0].blendingEnabled = YES; + pipeline_state_descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + pipeline_state_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; + pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; renderer->pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor error:&error]; } - if (error != nil){ - return(false); - } + Assert(error == nil); // NOTE(yuval): Create the command queue renderer->command_queue = [renderer->device newCommandQueue]; @@ -266,17 +169,20 @@ metal_init(Metal_Renderer *renderer, MTKView *view){ options:options]; } - return(true); + // NOTE(yuval): Create a capture scope for gpu frame capture + renderer->capture_scope = [[MTLCaptureManager sharedCaptureManager] + newCaptureScopeWithDevice:renderer->device]; + renderer->capture_scope.label = @"4coder Metal Capture Scope"; } function void metal_render(Metal_Renderer *renderer, Render_Target *t){ - static const AAPLVertex triangleVertices[] = { - // 2D positions, RGBA colors - { { 200, 100 }, { 1, 0, 0, 1 } }, - { { 100, 100 }, { 0, 1, 0, 1 } }, - { { 150, 200 }, { 0, 0, 1, 1 } }, - }; + [renderer->capture_scope beginScope]; + + i32 width = t->width; + i32 height = t->height; + + Font_Set* font_set = (Font_Set*)t->font_set; // NOTE(yuval): Create the command buffer id command_buffer = [renderer->command_queue commandBuffer]; @@ -285,79 +191,95 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ // NOTE(yuval): Obtain the render pass descriptor from the renderer's view MTLRenderPassDescriptor *render_pass_descriptor = renderer->view.currentRenderPassDescriptor; if (render_pass_descriptor != nil){ + render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0f, 0.0f, 1.0f, 1.0f); + // NOTE(yuval): Create the render command encoder id render_encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; render_encoder.label = @"4coder Render Encoder"; // NOTE(yuval): Set the region of the drawable to draw into - [render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)t->width, (double)t->height, 0.0, 1.0}]; + [render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)width, (double)height, 0.0, 1.0}]; // NOTE(yuval): Set the render pipeline to use for drawing [render_encoder setRenderPipelineState:renderer->pipeline_state]; - // NOTE(yuval): Pass in the parameter data - [render_encoder setVertexBytes:triangleVertices - length:sizeof(triangleVertices) - atIndex:AAPLVertexInputIndexVertices]; - -#if 0 - vector_uint2 viewport_size = {(u32)t->width, (u32)t->height}; - [render_encoder setVertexBytes:&viewport_size - length:sizeof(viewport_size) - atIndex:AAPLVertexInputIndexViewportSize]; -#else - float left = 0, right = (float)t->width; - float bottom = 0, top = (float)t->height; + // NOTE(yuval): Calculate and pass in the projection matrix + float left = 0, right = (float)width; + float bottom = (float)height, top = 0; float near_depth = -1.0f, far_depth = 1.0f; - float m[16] = { + float proj[16] = { 2.0f / (right - left), 0.0f, 0.0f, 0.0f, 0.0f, 2.0f / (top - bottom), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f / (far_depth - near_depth), 0.0f, -((right + left) / (right - left)), -((top + bottom) / (top - bottom)), - (-near_depth) / (far_depth - near_depth), 1.0f + -(near_depth / (far_depth - near_depth)), 1.0f }; - float sLength = 1.0f / (right - left); - float sHeight = 1.0f / (top - bottom); - float sDepth = 1.0f / (far_depth - near_depth); - - simd::float4 P; - simd::float4 Q; - simd::float4 R; - simd::float4 S; - - P.x = 2.0f * sLength; - P.y = 0.0f; - P.z = 0.0f; - P.w = 0.0f; - - Q.x = 0.0f; - Q.y = 2.0f * sHeight; - Q.z = 0.0f; - Q.w = 0.0f; - - R.x = 0.0f; - R.y = 0.0f; - R.z = sDepth; - R.w = 0.0f; - - S.x = -((right + left) / (right - left)); - S.y = -((top + bottom) / (top - bottom)); - S.z = -near_depth * sDepth; - S.w = 1.0f; - - simd_float4x4 proj = simd::float4x4(P, Q, R, S); - - [render_encoder setVertexBytes:&proj - length:sizeof(proj) - atIndex:AAPLVertexInputIndexViewportSize]; -#endif - - // NOTE(yuval): Draw the triangle - [render_encoder drawPrimitives:MTLPrimitiveTypeTriangle - vertexStart:0 - vertexCount:3]; + for (Render_Group *group = t->group_first; + group; + group = group->next){ + // NOTE(yuval): Set scissor rect + { + Rect_i32 box = Ri32(group->clip_box); + MTLScissorRect scissor_rect; + + CGSize frame = [renderer->view drawableSize]; + printf("Drawable Size - w:%f h:%f\n", frame.width, frame.height); + + NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), frame.width - 1); + NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), frame.width); + NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), frame.height - 1); + NSUInteger y1 = (NSUInteger)Min(Max(0, box.y1), frame.height); + + scissor_rect.x = x0; + scissor_rect.y = y0; + scissor_rect.width = (x1 - x0); + scissor_rect.height = (y1 - y0); + + [render_encoder setScissorRect:scissor_rect]; + } + + i32 vertex_count = group->vertex_list.vertex_count; + if (vertex_count > 0){ + // TODO(yuval): Bind a texture + { + Face* face = font_set_face_from_id(font_set, group->face_id); + if (face != 0){ + // TODO(yuval): Bind face texture + } else{ + // TODO(yuval): Bind default texture + } + } + + // NOTE(yuval): Copy the vertex data to the vertex buffer + { + u8 *cursor = (u8*)[renderer->buffer contents]; + for (Render_Vertex_Array_Node *node = group->vertex_list.first; + node; + node = node->next){ + i32 size = node->vertex_count * sizeof(*node->vertices); + memcpy(cursor, node->vertices, size); + cursor += size; + } + } + + // NOTE(yuval): Pass the vertex buffer to the vertex shader + [render_encoder setVertexBuffer:renderer->buffer + offset:0 + atIndex:0]; + + // NOTE(yuval): Pass the projection matrix to the vertex shader + [render_encoder setVertexBytes:&proj + length:sizeof(proj) + atIndex:1]; + + // NOTE(yuval): Draw the vertices + [render_encoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:vertex_count]; + } + } [render_encoder endEncoding]; @@ -366,4 +288,6 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ } [command_buffer commit]; + + [renderer->capture_scope endScope]; } \ No newline at end of file diff --git a/metal/AAPLShaderTypes.h b/metal/AAPLShaderTypes.h index be46a81a..a464b446 100644 --- a/metal/AAPLShaderTypes.h +++ b/metal/AAPLShaderTypes.h @@ -12,14 +12,6 @@ Header containing types and enum constants shared between Metal shaders and C/Ob #include #define clamp(a,x,b) clamp_((a),(x),(b)) -// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs -// match Metal API buffer set calls. -typedef enum AAPLVertexInputIndex -{ - AAPLVertexInputIndexVertices = 0, - AAPLVertexInputIndexViewportSize = 1, -} AAPLVertexInputIndex; - // This structure defines the layout of vertices sent to the vertex // shader. This header is shared between the .metal shader and C code, to guarantee that // the layout of the vertex array in the C code matches the layout that the .metal diff --git a/opengl/4ed_opengl_render.cpp b/opengl/4ed_opengl_render.cpp index 7c2d5524..2246eaeb 100644 --- a/opengl/4ed_opengl_render.cpp +++ b/opengl/4ed_opengl_render.cpp @@ -129,7 +129,7 @@ sd = abs(sd + half_thickness) - half_thickness; float shape_value = 1.0 - smoothstep(-1.0, 0.0, sd); shape_value *= has_thickness; -out_color = vec4(fragment_color.xyz, fragment_color.a*(sample_value + shape_value)); +out_color = fragment_color;//vec4(fragment_color.xyz, fragment_color.a*(sample_value + shape_value)); } )foo"; diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index cae3a944..3cf2c7e6 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -440,7 +440,7 @@ this only gets called for window creation and other extraordinary events. [NSApp terminate:nil]; } - // mac_gl_render(&target); + //mac_gl_render(&target); mac_metal_render(&target); mac_vars.first = false; From a34d95b848962e6144da9bce6aa165410741ee94 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 7 Jan 2020 02:55:28 +0200 Subject: [PATCH 45/67] Metal renderer is now working (no textures yet). --- metal/4ed_metal_render.mm | 302 +++++++++++++++++++++++++--------- platform_mac/mac_4ed_metal.mm | 14 +- 2 files changed, 231 insertions(+), 85 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index cf760786..fb21d113 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -8,17 +8,26 @@ #include "AAPLShaderTypes.h" #define function static -struct Metal_Renderer{ - MTKView *view; - - id device; - id pipeline_state; - id command_queue; - id buffer; - id capture_scope; -}; +//////////////////////////////// -global_const u32 metal_max_vertices = (1<<16); +// TODO(yuval): Convert this to a struct when I handle my own caching solution +@interface Metal_Buffer : NSObject +@property (nonatomic, strong) id buffer; +@property (nonatomic, readonly) u32 size; +@property (nonatomic, assign) NSTimeInterval last_reuse_time; + +- (instancetype)initWithSize:(u32)size usingDevice:(id)device; +@end + +@interface Metal_Renderer : NSObject +@property (nonatomic) Render_Target *target; + +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtkView; +- (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size; +- (void)add_reusable_buffer:(Metal_Buffer*)buffer; +@end + +//////////////////////////////// global_const char *metal__shaders_source = R"( #include @@ -29,10 +38,10 @@ using namespace metal; //////////////////////////////// typedef struct{ -packed_float2 xy; - packed_float3 uvw; -uint32_t color; -float half_thickness; +float2 xy [[attribute(0)]]; + float3 uvw [[attribute(1)]]; +uint32_t color [[attribute(2)]]; +float half_thickness [[attribute(3)]]; } Vertex; // NOTE(yuval): Vertex shader outputs and fragment shader inputs @@ -51,33 +60,32 @@ float4 position [[position]]; //////////////////////////////// vertex Rasterizer_Data -vertex_shader(uint vertex_id [[vertex_id]], constant Vertex *vertices [[buffer(0)]], +vertex_shader(Vertex in [[stage_in]], constant float4x4 &proj [[buffer(1)]]){ - constant Vertex *in = &vertices[vertex_id]; Rasterizer_Data out; // NOTE(yuval): Calculate position in NDC - out.position = proj * float4(in->xy, 0.0, 1.0); + out.position = proj * float4(in.xy, 0.0, 1.0); // NOTE(yuval): Convert color to float4 format - out.color.b = ((float((in->color ) & 0xFFu)) / 255.0); - out.color.g = ((float((in->color >> 8u) & 0xFFu)) / 255.0); - out.color.r = ((float((in->color >> 16u) & 0xFFu)) / 255.0); - out.color.a = ((float((in->color >> 24u) & 0xFFu)) / 255.0); + out.color.b = ((float((in.color ) & 0xFFu)) / 255.0); + out.color.g = ((float((in.color >> 8u) & 0xFFu)) / 255.0); + out.color.r = ((float((in.color >> 16u) & 0xFFu)) / 255.0); + out.color.a = ((float((in.color >> 24u) & 0xFFu)) / 255.0); // NOTE(yuval): Pass uvw coordinates to the fragment shader - out.uvw = in->uvw; + out.uvw = in.uvw; // NOTE(yuval): Calculate adjusted half dim - float2 center = in->uvw.xy; - float2 half_dim = abs(in->xy - center); - out.adjusted_half_dim = (half_dim - in->uvw.zz + float2(0.5, 0.5)); + float2 center = in.uvw.xy; + float2 half_dim = abs(in.xy - center); + out.adjusted_half_dim = (half_dim - in.uvw.zz + float2(0.5, 0.5)); // NOTE(yuval): Pass half_thickness to the fragment shader - out.half_thickness = in->half_thickness; + out.half_thickness = in.half_thickness; // NOTE(yuval): Pass xy to the fragment shader - out.xy = in->xy; + out.xy = in.xy; return(out); } @@ -113,12 +121,48 @@ shape_value *= has_thickness; } )"; -function void -metal_init(Metal_Renderer *renderer, MTKView *view){ +//////////////////////////////// + +@implementation Metal_Buffer +- (instancetype)initWithSize:(u32)size usingDevice:(id)device{ + self = [super init]; + if (self == nil){ + return(nil); + } + + // NOTE(yuval): Create the vertex buffer + MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; + _buffer = [device newBufferWithLength:size options:options]; + _size = size; + + // NOTE(yuval): Set the last_reuse_time to the current time + _last_reuse_time = [NSDate date].timeIntervalSince1970; + + return(self); +} +@end + +//////////////////////////////// + +@implementation Metal_Renderer{ + id device; + id pipeline_state; + id command_queue; + id capture_scope; + + NSMutableArray *buffer_cache; + NSTimeInterval last_buffer_cache_purge_time; +} + +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{ + self = [super init]; + if (self == nil){ + return(nil); + } + NSError *error = nil; - renderer->view = view; - renderer->device = view.device; + device = mtk_view.device; // NOTE(yuval): Compile the shaders id vertex_function = nil; @@ -129,7 +173,7 @@ metal_init(Metal_Renderer *renderer, MTKView *view){ MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; options.fastMathEnabled = YES; - id shader_library = [renderer->device newLibraryWithSource:shaders_source_str + id shader_library = [device newLibraryWithSource:shaders_source_str options:options error:&error]; vertex_function = [shader_library newFunctionWithName:@"vertex_shader"]; fragment_function = [shader_library newFunctionWithName:@"fragment_shader"]; @@ -141,55 +185,78 @@ metal_init(Metal_Renderer *renderer, MTKView *view){ // NOTE(yuval): Configure the pipeline descriptor { + MTLVertexDescriptor *vertexDescriptor = [MTLVertexDescriptor vertexDescriptor]; + vertexDescriptor.attributes[0].offset = OffsetOfMember(Render_Vertex, xy); + vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; // position + vertexDescriptor.attributes[0].bufferIndex = 0; + vertexDescriptor.attributes[1].offset = OffsetOfMember(Render_Vertex, uvw); + vertexDescriptor.attributes[1].format = MTLVertexFormatFloat3; // texCoords + vertexDescriptor.attributes[1].bufferIndex = 0; + vertexDescriptor.attributes[2].offset = OffsetOfMember(Render_Vertex, color); + vertexDescriptor.attributes[2].format = MTLVertexFormatUInt; // color + vertexDescriptor.attributes[2].bufferIndex = 0; + vertexDescriptor.attributes[3].offset = OffsetOfMember(Render_Vertex, half_thickness); + vertexDescriptor.attributes[3].format = MTLVertexFormatFloat; // position + vertexDescriptor.attributes[3].bufferIndex = 0; + vertexDescriptor.layouts[0].stepRate = 1; + vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; + vertexDescriptor.layouts[0].stride = sizeof(Render_Vertex); + MTLRenderPipelineDescriptor *pipeline_state_descriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipeline_state_descriptor.label = @"4coder Metal Renderer Pipeline"; pipeline_state_descriptor.vertexFunction = vertex_function; pipeline_state_descriptor.fragmentFunction = fragment_function; - pipeline_state_descriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; + pipeline_state_descriptor.vertexDescriptor = vertexDescriptor; + pipeline_state_descriptor.colorAttachments[0].pixelFormat = mtk_view.colorPixelFormat; pipeline_state_descriptor.colorAttachments[0].blendingEnabled = YES; + pipeline_state_descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; + pipeline_state_descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; pipeline_state_descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; pipeline_state_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; - pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + /*pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; + pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;*/ - renderer->pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor + pipeline_state = [device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor error:&error]; } Assert(error == nil); // NOTE(yuval): Create the command queue - renderer->command_queue = [renderer->device newCommandQueue]; + command_queue = [device newCommandQueue]; - // NOTE(yuval): Create the vertex buffer - { - u32 buffer_size = (metal_max_vertices * sizeof(Render_Vertex)); - MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; - renderer->buffer = [renderer->device newBufferWithLength:buffer_size - options:options]; - } + // NOTE(yuval): Initialize buffer caching + buffer_cache = [NSMutableArray array]; + last_buffer_cache_purge_time = [NSDate date].timeIntervalSince1970; // NOTE(yuval): Create a capture scope for gpu frame capture - renderer->capture_scope = [[MTLCaptureManager sharedCaptureManager] - newCaptureScopeWithDevice:renderer->device]; - renderer->capture_scope.label = @"4coder Metal Capture Scope"; + capture_scope = [[MTLCaptureManager sharedCaptureManager] + newCaptureScopeWithDevice:device]; + capture_scope.label = @"4coder Metal Capture Scope"; + + return(self); } -function void -metal_render(Metal_Renderer *renderer, Render_Target *t){ - [renderer->capture_scope beginScope]; +- (void)mtkView:(nonnull MTKView*)view drawableSizeWillChange:(CGSize)size{ + // NOTE(yuval): Nothing to do here because we use the render target's dimentions for rendering +} + +- (void)drawInMTKView:(nonnull MTKView*)view{ + printf("Metal Renderer Draw!\n"); - i32 width = t->width; - i32 height = t->height; + [capture_scope beginScope]; - Font_Set* font_set = (Font_Set*)t->font_set; + i32 width = _target->width; + i32 height = _target->height; + + Font_Set* font_set = (Font_Set*)_target->font_set; // NOTE(yuval): Create the command buffer - id command_buffer = [renderer->command_queue commandBuffer]; + id command_buffer = [command_queue commandBuffer]; command_buffer.label = @"4coder Metal Render Command"; // NOTE(yuval): Obtain the render pass descriptor from the renderer's view - MTLRenderPassDescriptor *render_pass_descriptor = renderer->view.currentRenderPassDescriptor; + MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor; if (render_pass_descriptor != nil){ render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0f, 0.0f, 1.0f, 1.0f); @@ -202,7 +269,7 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ [render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)width, (double)height, 0.0, 1.0}]; // NOTE(yuval): Set the render pipeline to use for drawing - [render_encoder setRenderPipelineState:renderer->pipeline_state]; + [render_encoder setRenderPipelineState:pipeline_state]; // NOTE(yuval): Calculate and pass in the projection matrix float left = 0, right = (float)width; @@ -216,22 +283,44 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ -(near_depth / (far_depth - near_depth)), 1.0f }; - for (Render_Group *group = t->group_first; + // NOTE(yuval): Calculate required vertex buffer size + i32 all_vertex_count = 0; + for (Render_Group *group = _target->group_first; group; group = group->next){ + all_vertex_count += group->vertex_list.vertex_count; + } + + u32 vertex_buffer_size = (all_vertex_count * sizeof(Render_Vertex)); + + // NOTE(yuval): Find & Get a vertex buffer matching the required size + Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size]; + + // NOTE(yuval): Pass the vertex buffer to the vertex shader + [render_encoder setVertexBuffer:buffer.buffer + offset:0 + atIndex:0]; + + // NOTE(yuval): Pass the projection matrix to the vertex shader + [render_encoder setVertexBytes:&proj + length:sizeof(proj) + atIndex:1]; + + u32 buffer_offset = 0; + i32 count = 0; + for (Render_Group *group = _target->group_first; + group; + group = group->next, ++count){ // NOTE(yuval): Set scissor rect { Rect_i32 box = Ri32(group->clip_box); + + NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), width - 1); + NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), width); + NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), height - 1); + NSUInteger y1 = (NSUInteger)Min(Max(0, box.y1), height); + MTLScissorRect scissor_rect; - - CGSize frame = [renderer->view drawableSize]; - printf("Drawable Size - w:%f h:%f\n", frame.width, frame.height); - - NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), frame.width - 1); - NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), frame.width); - NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), frame.height - 1); - NSUInteger y1 = (NSUInteger)Min(Max(0, box.y1), frame.height); - scissor_rect.x = x0; scissor_rect.y = y0; scissor_rect.width = (x1 - x0); @@ -254,7 +343,9 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ // NOTE(yuval): Copy the vertex data to the vertex buffer { - u8 *cursor = (u8*)[renderer->buffer contents]; + + u8 *group_buffer_contents = (u8*)[buffer.buffer contents] + buffer_offset; + u8 *cursor = group_buffer_contents; for (Render_Vertex_Array_Node *node = group->vertex_list.first; node; node = node->next){ @@ -262,32 +353,85 @@ metal_render(Metal_Renderer *renderer, Render_Target *t){ memcpy(cursor, node->vertices, size); cursor += size; } + + NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents); + NSRange modify_range = NSMakeRange(buffer_offset, data_size); + [buffer.buffer didModifyRange:modify_range]; } - // NOTE(yuval): Pass the vertex buffer to the vertex shader - [render_encoder setVertexBuffer:renderer->buffer - offset:0 - atIndex:0]; - - // NOTE(yuval): Pass the projection matrix to the vertex shader - [render_encoder setVertexBytes:&proj - length:sizeof(proj) - atIndex:1]; + // NOTE(yuval): Set the vertex buffer offset to the beginning of the group's vertices + [render_encoder setVertexBufferOffset:buffer_offset atIndex:0]; // NOTE(yuval): Draw the vertices [render_encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:vertex_count]; + + buffer_offset += (vertex_count * sizeof(Render_Vertex)); //((((vertex_count * sizeof(Render_Vertex)) + 256) / 256) * 256); } } [render_encoder endEncoding]; // NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable - [command_buffer presentDrawable:renderer->view.currentDrawable]; + [command_buffer presentDrawable:view.currentDrawable]; + + [command_buffer addCompletedHandler:^(id){ + dispatch_async(dispatch_get_main_queue(), ^{ + [self add_reusable_buffer:buffer]; + }); + }]; } + // NOTE(yuval): Finalize rendering here and push the command buffer to the GPU [command_buffer commit]; - [renderer->capture_scope endScope]; -} \ No newline at end of file + [capture_scope endScope]; +} + +- (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size{ + // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::dequeueReusableBufferOfLength in imgui_impl_metal.mm + + NSTimeInterval now = [NSDate date].timeIntervalSince1970; + + // NOTE(yuval): Purge old buffers that haven't been useful for a while + if ((now - last_buffer_cache_purge_time) > 1.0){ + NSMutableArray *survivors = [NSMutableArray array]; + for (Metal_Buffer *candidate in buffer_cache){ + if (candidate.last_reuse_time > last_buffer_cache_purge_time){ + [survivors addObject:candidate]; + } + } + + buffer_cache = [survivors mutableCopy]; + last_buffer_cache_purge_time = now; + } + + // NOTE(yuval): See if we have a buffer we can reuse + Metal_Buffer *best_candidate = 0; + for (Metal_Buffer *candidate in buffer_cache){ + if ((candidate.size >= size) && ((!best_candidate) || (best_candidate.last_reuse_time > candidate.last_reuse_time))){ + best_candidate = candidate; + } + } + + Metal_Buffer *result; + if (best_candidate){ + [buffer_cache removeObject:best_candidate]; + best_candidate.last_reuse_time = now; + result = best_candidate; + } else{ + result = [[Metal_Buffer alloc] initWithSize:size usingDevice:device]; + } + + // NOTE(yuval): No luck; make a new buffer + + return result; +} + +- (void)add_reusable_buffer:(Metal_Buffer*)buffer{ + // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::enqueueReusableBuffer in imgui_impl_metal.mm + + [buffer_cache addObject:buffer]; +} +@end diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index ff69d8d3..4970feab 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -1,6 +1,6 @@ #import "metal/4ed_metal_render.mm" -global Metal_Renderer metal_renderer; +global Metal_Renderer *metal_renderer; global MTKView *metal_view; function void @@ -10,22 +10,24 @@ mac_metal_init(NSWindow *window){ metal_view = [[MTKView alloc] initWithFrame:[content_view bounds]]; [metal_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [metal_view setPaused:YES]; + [metal_view setEnableSetNeedsDisplay:NO]; metal_view.device = MTLCreateSystemDefaultDevice(); // NOTE(yuval): Add the Metal view as a subview of the window [content_view addSubview:metal_view]; - // NOTE(yuval): Create the Metal renderer - //metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; - metal_init(&metal_renderer, metal_view); + // NOTE(yuval): Create the Metal renderer and set it as the Metal view's delegate + metal_renderer = [[Metal_Renderer alloc] initWithMetalKitView:metal_view]; + metal_view.delegate = metal_renderer; } function void mac_metal_render(Render_Target* target){ u64 begin_time = system_now_time(); - //[metal_renderer drawInMTKView:metal_view]; - metal_render(&metal_renderer, target); + metal_renderer.target = target; + [metal_view draw]; u64 end_time = system_now_time(); printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); } \ No newline at end of file From a18ef3197a7ecfaa39a48ac5e17ea6ac3d56e53c Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 8 Jan 2020 01:45:36 +0200 Subject: [PATCH 46/67] Implemented my own vertex buffers management, also started working on textures. --- metal/4ed_metal_render.mm | 163 ++++++++++++++++++++------------------ platform_mac/mac_4ed.mm | 4 +- 2 files changed, 88 insertions(+), 79 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index fb21d113..a1d8e7f0 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -10,14 +10,19 @@ //////////////////////////////// -// TODO(yuval): Convert this to a struct when I handle my own caching solution -@interface Metal_Buffer : NSObject -@property (nonatomic, strong) id buffer; -@property (nonatomic, readonly) u32 size; -@property (nonatomic, assign) NSTimeInterval last_reuse_time; +struct Metal_Buffer{ + Node node; + + id buffer; + u32 size; + u64 last_reuse_time; +}; -- (instancetype)initWithSize:(u32)size usingDevice:(id)device; -@end +struct Metal_Texture{ + +}; + +//////////////////////////////// @interface Metal_Renderer : NSObject @property (nonatomic) Render_Target *target; @@ -39,7 +44,7 @@ using namespace metal; typedef struct{ float2 xy [[attribute(0)]]; - float3 uvw [[attribute(1)]]; +float3 uvw [[attribute(1)]]; uint32_t color [[attribute(2)]]; float half_thickness [[attribute(3)]]; } Vertex; @@ -50,11 +55,11 @@ typedef struct{ float4 position [[position]]; // NOTE(yuval): Fragment shader inputs - float4 color; - float3 uvw; - float2 xy; - float2 adjusted_half_dim; - float half_thickness; +float4 color; +float3 uvw; +float2 xy; +float2 adjusted_half_dim; +float half_thickness; } Rasterizer_Data; //////////////////////////////// @@ -62,32 +67,32 @@ float4 position [[position]]; vertex Rasterizer_Data vertex_shader(Vertex in [[stage_in]], constant float4x4 &proj [[buffer(1)]]){ - Rasterizer_Data out; - - // NOTE(yuval): Calculate position in NDC - out.position = proj * float4(in.xy, 0.0, 1.0); - - // NOTE(yuval): Convert color to float4 format - out.color.b = ((float((in.color ) & 0xFFu)) / 255.0); - out.color.g = ((float((in.color >> 8u) & 0xFFu)) / 255.0); - out.color.r = ((float((in.color >> 16u) & 0xFFu)) / 255.0); - out.color.a = ((float((in.color >> 24u) & 0xFFu)) / 255.0); - - // NOTE(yuval): Pass uvw coordinates to the fragment shader - out.uvw = in.uvw; - - // NOTE(yuval): Calculate adjusted half dim - float2 center = in.uvw.xy; - float2 half_dim = abs(in.xy - center); - out.adjusted_half_dim = (half_dim - in.uvw.zz + float2(0.5, 0.5)); - - // NOTE(yuval): Pass half_thickness to the fragment shader - out.half_thickness = in.half_thickness; - - // NOTE(yuval): Pass xy to the fragment shader - out.xy = in.xy; - - return(out); +Rasterizer_Data out; + +// NOTE(yuval): Calculate position in NDC +out.position = proj * float4(in.xy, 0.0, 1.0); + +// NOTE(yuval): Convert color to float4 format +out.color.b = ((float((in.color ) & 0xFFu)) / 255.0); +out.color.g = ((float((in.color >> 8u) & 0xFFu)) / 255.0); +out.color.r = ((float((in.color >> 16u) & 0xFFu)) / 255.0); +out.color.a = ((float((in.color >> 24u) & 0xFFu)) / 255.0); + +// NOTE(yuval): Pass uvw coordinates to the fragment shader +out.uvw = in.uvw; + +// NOTE(yuval): Calculate adjusted half dim +float2 center = in.uvw.xy; +float2 half_dim = abs(in.xy - center); +out.adjusted_half_dim = (half_dim - in.uvw.zz + float2(0.5, 0.5)); + +// NOTE(yuval): Pass half_thickness to the fragment shader +out.half_thickness = in.half_thickness; + +// NOTE(yuval): Pass xy to the fragment shader +out.xy = in.xy; + +return(out); } //////////////////////////////// @@ -115,32 +120,28 @@ sd = (abs(sd + in.half_thickness) - in.half_thickness); float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd)); shape_value *= has_thickness; - // TOOD(yuval): Add sample_value to alpha - float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value)); - return(out_color); +// TOOD(yuval): Add sample_value to alpha +float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value)); +return(out_color); } )"; //////////////////////////////// -@implementation Metal_Buffer -- (instancetype)initWithSize:(u32)size usingDevice:(id)device{ - self = [super init]; - if (self == nil){ - return(nil); - } +function Metal_Buffer* +metal__make_buffer(u32 size, id device){ + Metal_Buffer *result = (Metal_Buffer*)malloc(sizeof(Metal_Buffer)); // NOTE(yuval): Create the vertex buffer MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; - _buffer = [device newBufferWithLength:size options:options]; - _size = size; + result->buffer = [device newBufferWithLength:size options:options]; + result->size = size; // NOTE(yuval): Set the last_reuse_time to the current time - _last_reuse_time = [NSDate date].timeIntervalSince1970; + result->last_reuse_time = system_now_time(); - return(self); + return result; } -@end //////////////////////////////// @@ -150,8 +151,8 @@ shape_value *= has_thickness; id command_queue; id capture_scope; - NSMutableArray *buffer_cache; - NSTimeInterval last_buffer_cache_purge_time; + Node buffer_cache; + u64 last_buffer_cache_purge_time; } - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{ @@ -182,6 +183,7 @@ shape_value *= has_thickness; } Assert(error == nil); + Assert((vertex_function != nil) && (fragment_function != nil)); // NOTE(yuval): Configure the pipeline descriptor { @@ -226,8 +228,8 @@ shape_value *= has_thickness; command_queue = [device newCommandQueue]; // NOTE(yuval): Initialize buffer caching - buffer_cache = [NSMutableArray array]; - last_buffer_cache_purge_time = [NSDate date].timeIntervalSince1970; + dll_init_sentinel(&buffer_cache); + last_buffer_cache_purge_time = system_now_time(); // NOTE(yuval): Create a capture scope for gpu frame capture capture_scope = [[MTLCaptureManager sharedCaptureManager] @@ -249,7 +251,7 @@ shape_value *= has_thickness; i32 width = _target->width; i32 height = _target->height; - Font_Set* font_set = (Font_Set*)_target->font_set; + Font_Set *font_set = (Font_Set*)_target->font_set; // NOTE(yuval): Create the command buffer id command_buffer = [command_queue commandBuffer]; @@ -297,7 +299,7 @@ shape_value *= has_thickness; Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size]; // NOTE(yuval): Pass the vertex buffer to the vertex shader - [render_encoder setVertexBuffer:buffer.buffer + [render_encoder setVertexBuffer:buffer->buffer offset:0 atIndex:0]; @@ -344,7 +346,7 @@ shape_value *= has_thickness; // NOTE(yuval): Copy the vertex data to the vertex buffer { - u8 *group_buffer_contents = (u8*)[buffer.buffer contents] + buffer_offset; + u8 *group_buffer_contents = (u8*)[buffer->buffer contents] + buffer_offset; u8 *cursor = group_buffer_contents; for (Render_Vertex_Array_Node *node = group->vertex_list.first; node; @@ -356,7 +358,7 @@ shape_value *= has_thickness; NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents); NSRange modify_range = NSMakeRange(buffer_offset, data_size); - [buffer.buffer didModifyRange:modify_range]; + [buffer->buffer didModifyRange:modify_range]; } // NOTE(yuval): Set the vertex buffer offset to the beginning of the group's vertices @@ -367,7 +369,7 @@ shape_value *= has_thickness; vertexStart:0 vertexCount:vertex_count]; - buffer_offset += (vertex_count * sizeof(Render_Vertex)); //((((vertex_count * sizeof(Render_Vertex)) + 256) / 256) * 256); + buffer_offset += (vertex_count * sizeof(Render_Vertex)); } } @@ -392,46 +394,53 @@ shape_value *= has_thickness; - (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size{ // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::dequeueReusableBufferOfLength in imgui_impl_metal.mm - NSTimeInterval now = [NSDate date].timeIntervalSince1970; + u64 now = system_now_time(); // NOTE(yuval): Purge old buffers that haven't been useful for a while - if ((now - last_buffer_cache_purge_time) > 1.0){ - NSMutableArray *survivors = [NSMutableArray array]; - for (Metal_Buffer *candidate in buffer_cache){ - if (candidate.last_reuse_time > last_buffer_cache_purge_time){ - [survivors addObject:candidate]; + if ((now - last_buffer_cache_purge_time) > 1000000){ + Node prev_buffer_cache = buffer_cache; + dll_init_sentinel(&buffer_cache); + + for (Node *node = prev_buffer_cache.next; + node != &buffer_cache; + node = node->next){ + Metal_Buffer *candidate = CastFromMember(Metal_Buffer, node, node); + if (candidate->last_reuse_time > last_buffer_cache_purge_time){ + dll_insert(&buffer_cache, node); } } - buffer_cache = [survivors mutableCopy]; last_buffer_cache_purge_time = now; } // NOTE(yuval): See if we have a buffer we can reuse Metal_Buffer *best_candidate = 0; - for (Metal_Buffer *candidate in buffer_cache){ - if ((candidate.size >= size) && ((!best_candidate) || (best_candidate.last_reuse_time > candidate.last_reuse_time))){ + for (Node *node = buffer_cache.next; + node != &buffer_cache; + node = node->next){ + Metal_Buffer *candidate = CastFromMember(Metal_Buffer, node, node); + if ((candidate->size >= size) && ((!best_candidate) || (best_candidate->last_reuse_time > candidate->last_reuse_time))){ best_candidate = candidate; } } Metal_Buffer *result; if (best_candidate){ - [buffer_cache removeObject:best_candidate]; - best_candidate.last_reuse_time = now; + // NOTE(yuval): A best candidate has been found! Remove it from the buffer list and set its last reuse time. + dll_remove(&best_candidate->node); + best_candidate->last_reuse_time = now; result = best_candidate; } else{ - result = [[Metal_Buffer alloc] initWithSize:size usingDevice:device]; + // NOTE(yuval): No luck; make a new buffer. + result = metal__make_buffer(size, device); } - // NOTE(yuval): No luck; make a new buffer - return result; } - (void)add_reusable_buffer:(Metal_Buffer*)buffer{ // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::enqueueReusableBuffer in imgui_impl_metal.mm - [buffer_cache addObject:buffer]; + dll_insert(&buffer_cache, &buffer->node); } @end diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 3cf2c7e6..6ea09e72 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -460,8 +460,8 @@ this only gets called for window creation and other extraordinary events. return(YES); } -- (void)keyDown:(NSEvent *)event{ - NSString* characters = [event characters]; +- (void)keyDown:(NSEvent*)event{ + NSString *characters = [event characters]; if ([characters length] != 0) { u32 character_code = [characters characterAtIndex:0]; From 521fb82113b9d6d49e30774bf00a6d173ee976ff Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 8 Jan 2020 21:44:47 +0200 Subject: [PATCH 47/67] The Metal renderer is now fully functioning. --- metal/4ed_metal_render.mm | 82 ++++++++++++++++++++++++++++--- platform_mac/mac_4ed.mm | 8 +++ platform_mac/mac_4ed_functions.mm | 6 +-- platform_mac/mac_4ed_metal.mm | 20 ++++++++ 4 files changed, 105 insertions(+), 11 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index a1d8e7f0..147af602 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -10,6 +10,8 @@ //////////////////////////////// +typedef id Metal_Texture; + struct Metal_Buffer{ Node node; @@ -18,22 +20,24 @@ struct Metal_Buffer{ u64 last_reuse_time; }; -struct Metal_Texture{ - -}; - //////////////////////////////// @interface Metal_Renderer : NSObject @property (nonatomic) Render_Target *target; - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtkView; +- (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind; +- (b32)fill_texture:(u32)texture kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data; - (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size; - (void)add_reusable_buffer:(Metal_Buffer*)buffer; @end //////////////////////////////// +global_const u32 metal__max_textures = 256; + +//////////////////////////////// + global_const char *metal__shaders_source = R"( #include #include @@ -106,11 +110,14 @@ return(result); } fragment float4 -fragment_shader(Rasterizer_Data in [[stage_in]]){ +fragment_shader(Rasterizer_Data in [[stage_in]], +texture2d_array in_texture [[texture(0)]]){ float has_thickness = step(0.49, in.half_thickness); -// float does_not_have_thickness = (1.0 - has_thickness); +float does_not_have_thickness = (1.0 - has_thickness); -// TODO(yuval): Sample texture here. +constexpr sampler texture_sampler(coord::normalized, min_filter::linear, mag_filter::linear, mip_filter::linear); +half sample_value = in_texture.sample(texture_sampler, in.uvw.xy, in.uvw.z).r; +sample_value *= does_not_have_thickness; float2 center = in.uvw.xy; float roundness = in.uvw.z; @@ -121,7 +128,7 @@ float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd)); shape_value *= has_thickness; // TOOD(yuval): Add sample_value to alpha -float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value)); +float4 out_color = float4(in.color.xyz, in.color.a * (sample_value + shape_value)); return(out_color); } )"; @@ -153,6 +160,9 @@ metal__make_buffer(u32 size, id device){ Node buffer_cache; u64 last_buffer_cache_purge_time; + + Metal_Texture *textures; + u32 next_texture_handle_index; } - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{ @@ -209,6 +219,7 @@ metal__make_buffer(u32 size, id device){ pipeline_state_descriptor.vertexFunction = vertex_function; pipeline_state_descriptor.fragmentFunction = fragment_function; pipeline_state_descriptor.vertexDescriptor = vertexDescriptor; + pipeline_state_descriptor.sampleCount = mtk_view.sampleCount; pipeline_state_descriptor.colorAttachments[0].pixelFormat = mtk_view.colorPixelFormat; pipeline_state_descriptor.colorAttachments[0].blendingEnabled = YES; pipeline_state_descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; @@ -231,6 +242,10 @@ metal__make_buffer(u32 size, id device){ dll_init_sentinel(&buffer_cache); last_buffer_cache_purge_time = system_now_time(); + // NOTE(yuval): Initialize the textures array + textures = (Metal_Texture*)system_memory_allocate(metal__max_textures * sizeof(Metal_Texture), file_name_line_number_lit_u8); + next_texture_handle_index = 0; + // NOTE(yuval): Create a capture scope for gpu frame capture capture_scope = [[MTLCaptureManager sharedCaptureManager] newCaptureScopeWithDevice:device]; @@ -338,6 +353,12 @@ metal__make_buffer(u32 size, id device){ Face* face = font_set_face_from_id(font_set, group->face_id); if (face != 0){ // TODO(yuval): Bind face texture + u32 texture_handle = face->texture; + Metal_Texture texture = textures[texture_handle]; + if (texture != 0){ + [render_encoder setFragmentTexture:texture + atIndex:0]; + } } else{ // TODO(yuval): Bind default texture } @@ -391,6 +412,51 @@ metal__make_buffer(u32 size, id device){ [capture_scope endScope]; } +- (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind{ + u32 handle = next_texture_handle_index; + + // NOTE(yuval): Create a texture descriptor + MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init]; + texture_descriptor.textureType = MTLTextureType2DArray; + texture_descriptor.pixelFormat = MTLPixelFormatR8Unorm; + texture_descriptor.width = dim.x; + texture_descriptor.height = dim.y; + texture_descriptor.depth = dim.z; + + // NOTE(yuval): Create the texture from the device using the descriptor and add it to the textures array + Metal_Texture texture = [device newTextureWithDescriptor:texture_descriptor]; + textures[handle] = texture; + + next_texture_handle_index += 1; + + return handle; +} + +- (b32)fill_texture:(u32)handle kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data{ + b32 result = false; + + if (data){ + Metal_Texture texture = textures[handle]; + + if (texture != 0){ + MTLRegion replace_region = { + {(NSUInteger)p.x, (NSUInteger)p.y, (NSUInteger)p.z}, + {(NSUInteger)dim.x, (NSUInteger)dim.y, (NSUInteger)dim.z} + }; + + // NOTE(yuval): Fill the texture with data + [texture replaceRegion:replace_region + mipmapLevel:0 + withBytes:data + bytesPerRow:dim.x]; + + result = true; + } + } + + return result; +} + - (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size{ // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::dequeueReusableBufferOfLength in imgui_impl_metal.mm diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 6ea09e72..05d07a2a 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -318,6 +318,7 @@ mac_file_can_be_made(u8* filename){ function void mac_resize(float width, float height){ if ((width > 0.0f) && (height > 0.0f)){ +#if 1 NSSize coord_size = NSMakeSize(width, height); NSSize backing_size = [mac_vars.view convertSizeToBacking:coord_size]; @@ -326,6 +327,13 @@ mac_resize(float width, float height){ target.width = (i32)backing_size.width; target.height = (i32)backing_size.height; +#else + mac_vars.width = (i32)width; + mac_vars.height = (i32)height; + + target.width = (i32)width; + target.height = (i32)height; +#endif } system_signal_step(0); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index aa3f619d..11dac318 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -30,7 +30,7 @@ system_get_path_sig(){ local_persist b32 has_stashed_4ed_path = false; if (!has_stashed_4ed_path){ local_const i32 binary_path_capacity = KB(32); - u8 *memory = (u8*)system_memory_allocate(binary_path_capacity, string_u8_litexpr(file_name_line_number)); + u8 *memory = (u8*)system_memory_allocate(binary_path_capacity, file_name_line_number_lit_u8); pid_t pid = getpid(); i32 size = proc_pidpath(pid, memory, binary_path_capacity); @@ -804,13 +804,13 @@ system_get_keyboard_modifiers_sig(){ function graphics_get_texture_sig(){ - u32 result = gl__get_texture(dim, texture_kind); + u32 result = mac_metal_get_texture(dim, texture_kind); return(result); } function graphics_fill_texture_sig(){ - b32 result = gl__fill_texture(texture_kind, texture, p, dim, data); + b32 result = mac_metal_fill_texture(texture_kind, texture, p, dim, data); return(result); } diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 4970feab..063e618a 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -12,6 +12,7 @@ mac_metal_init(NSWindow *window){ [metal_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [metal_view setPaused:YES]; [metal_view setEnableSetNeedsDisplay:NO]; + [metal_view setSampleCount:4]; metal_view.device = MTLCreateSystemDefaultDevice(); @@ -30,4 +31,23 @@ mac_metal_render(Render_Target* target){ [metal_view draw]; u64 end_time = system_now_time(); printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); +} + +function u32 +mac_metal_get_texture(Vec3_i32 dim, Texture_Kind texture_kind){ + u32 result = [metal_renderer get_texture_of_dim:dim + kind:texture_kind]; + + return result; +} + +function b32 +mac_metal_fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void *data){ + b32 result = [metal_renderer fill_texture:texture + kind:texture_kind + pos:p + dim:dim + data:data]; + + return result; } \ No newline at end of file From 912bcae8a7ff287d555b0fe375f5a134edb9b22a Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 9 Jan 2020 02:15:25 +0200 Subject: [PATCH 48/67] Started working on an abstraction for the macOS renderer implementations. --- metal/4ed_metal_render.mm | 7 +- opengl/4ed_opengl_render.cpp | 13 +-- platform_mac/mac_4ed.mm | 61 +++++++------- platform_mac/mac_4ed_functions.mm | 4 +- platform_mac/mac_4ed_metal.mm | 7 +- platform_mac/mac_4ed_opengl.mm | 129 ++++++++++++++++++------------ platform_mac/mac_4ed_renderer.h | 42 ++++++++++ platform_mac/mac_4ed_renderer.mm | 33 ++++++++ 8 files changed, 194 insertions(+), 102 deletions(-) create mode 100644 platform_mac/mac_4ed_renderer.h create mode 100644 platform_mac/mac_4ed_renderer.mm diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 147af602..d5126396 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -263,8 +263,10 @@ metal__make_buffer(u32 size, id device){ [capture_scope beginScope]; - i32 width = _target->width; - i32 height = _target->height; + // HACK(yuval): This is the best way I found to force valid width and height without drawing on the next drawing cycle (1 frame delay). + CGSize drawable_size = [view drawableSize]; + i32 width = (i32)Min(_target->width, drawable_size.width); + i32 height = (i32)Min(_target->height, drawable_size.height); Font_Set *font_set = (Font_Set*)_target->font_set; @@ -332,6 +334,7 @@ metal__make_buffer(u32 size, id device){ { Rect_i32 box = Ri32(group->clip_box); + NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), width - 1); NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), width); NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), height - 1); diff --git a/opengl/4ed_opengl_render.cpp b/opengl/4ed_opengl_render.cpp index 2246eaeb..69071502 100644 --- a/opengl/4ed_opengl_render.cpp +++ b/opengl/4ed_opengl_render.cpp @@ -129,7 +129,7 @@ sd = abs(sd + half_thickness) - half_thickness; float shape_value = 1.0 - smoothstep(-1.0, 0.0, sd); shape_value *= has_thickness; -out_color = fragment_color;//vec4(fragment_color.xyz, fragment_color.a*(sample_value + shape_value)); +out_color = vec4(fragment_color.xyz, fragment_color.a*(sample_value + shape_value)); } )foo"; @@ -285,8 +285,6 @@ gl_render(Render_Target *t){ t->free_texture_first = 0; t->free_texture_last = 0; - u32 all_vertex_count = 0; - u64 begin_draw = system_now_time(); for (Render_Group *group = t->group_first; group != 0; @@ -342,18 +340,9 @@ gl_render(Render_Target *t){ glDisableVertexAttribArray(gpu_program.vertex_c); glDisableVertexAttribArray(gpu_program.vertex_ht); } - - all_vertex_count += vertex_count; } - u64 end_draw = system_now_time(); - printf("Draw time: %fs\n", mac_get_time_diff_sec(begin_draw, end_draw)); - u64 begin_flush = system_now_time(); glFlush(); - u64 end_flush = system_now_time(); - printf("Flush time: %fs\n", mac_get_time_diff_sec(begin_flush, end_flush)); - - printf("Drawn %d Vertices\n", all_vertex_count); } // BOTTOM diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 05d07a2a..33f645f7 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -132,8 +132,6 @@ struct Mac_Input_Chunk{ //////////////////////////////// -//////////////////////////////// - typedef i32 Mac_Object_Kind; enum{ MacObjectKind_ERROR = 0, @@ -203,7 +201,12 @@ struct Mac_Vars { //////////////////////////////// +#include "mac_4ed_renderer.h" + +//////////////////////////////// + global Mac_Vars mac_vars; +global Mac_Renderer *renderer; global Render_Target target; global App_Functions app; @@ -269,30 +272,6 @@ mac_to_object(Plat_Handle handle){ //////////////////////////////// -#include -#include -#import "mac_4ed_opengl.mm" - -#import "mac_4ed_metal.mm" - -#include "4ed_font_provider_freetype.h" -#include "4ed_font_provider_freetype.cpp" - -#import "mac_4ed_functions.mm" - - - -//////////////////////////////// - -global Key_Code keycode_lookup_table[255]; - -function void -mac_key_code_init(void){ - -} - -//////////////////////////////// - function void mac_error_box(char *msg, b32 shutdown = true){ NSAlert *alert = [[[NSAlert alloc] init] autorelease]; @@ -309,6 +288,26 @@ mac_error_box(char *msg, b32 shutdown = true){ } } +//////////////////////////////// + +#import "mac_4ed_renderer.mm" + +#include "4ed_font_provider_freetype.h" +#include "4ed_font_provider_freetype.cpp" + +#import "mac_4ed_functions.mm" + +//////////////////////////////// + +global Key_Code keycode_lookup_table[255]; + +function void +mac_key_code_init(void){ + +} + +//////////////////////////////// + function b32 mac_file_can_be_made(u8* filename){ b32 result = access((char*)filename, W_OK) == 0; @@ -389,7 +388,7 @@ mac_resize(NSWindow *window){ } - (void)viewDidChangeBackingProperties{ - // NOTE(yuval): Screen scale factor calculation + // TODO(yuval): Screen scale factor calculation printf("Backing changed!\n"); mac_resize(mac_vars.window); } @@ -449,7 +448,8 @@ this only gets called for window creation and other extraordinary events. } //mac_gl_render(&target); - mac_metal_render(&target); + //mac_metal_render(&target); + renderer->render(renderer, &target); mac_vars.first = false; @@ -707,8 +707,9 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - mac_gl_init(mac_vars.window); - mac_metal_init(mac_vars.window); + //mac_gl_init(mac_vars.window); + //mac_metal_init(mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); mac_resize(w, h); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 11dac318..c1c37bae 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -804,13 +804,13 @@ system_get_keyboard_modifiers_sig(){ function graphics_get_texture_sig(){ - u32 result = mac_metal_get_texture(dim, texture_kind); + u32 result = renderer->get_texture(renderer, dim, texture_kind); return(result); } function graphics_fill_texture_sig(){ - b32 result = mac_metal_fill_texture(texture_kind, texture, p, dim, data); + b32 result = renderer->fill_texture(renderer, texture_kind, texture, p, dim, data); return(result); } diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 063e618a..ba44930b 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -4,7 +4,7 @@ global Metal_Renderer *metal_renderer; global MTKView *metal_view; function void -mac_metal_init(NSWindow *window){ +mac_metal_init(NSWindow *window, Render_Target *target){ // NOTE(yuval): Create Metal view NSView *content_view = [window contentView]; @@ -26,11 +26,8 @@ mac_metal_init(NSWindow *window){ function void mac_metal_render(Render_Target* target){ - u64 begin_time = system_now_time(); metal_renderer.target = target; [metal_view draw]; - u64 end_time = system_now_time(); - printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); } function u32 @@ -50,4 +47,4 @@ mac_metal_fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_ data:data]; return result; -} \ No newline at end of file +} diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index ba1f4dee..0a73c9c9 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -1,24 +1,38 @@ /* Mac OpenGL layer for 4coder */ +#include "mac_4ed_renderer.h" + +//////////////////////////////// +#include +#include + #include "opengl/4ed_opengl_defines.h" #define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0; #include "mac_4ed_opengl_funcs.h" -f64 mac_get_time_diff_sec(u64 begin, u64 end){ - f64 result = ((end - begin) / 1000000.0); - return result; -} +//////////////////////////////// #include "opengl/4ed_opengl_render.cpp" -@interface OpenGLView : NSOpenGLView -- (void)initGL; +//////////////////////////////// + +@interface OpenGL_View : NSOpenGLView +- (void)init_gl; - (void)render:(Render_Target*)target; @end -@implementation OpenGLView{ - b32 glIsInitialized; +//////////////////////////////// + +struct Mac_OpenGL_Renderer{ + Mac_Renderer base; + OpenGL_View *opengl_view; +}; + +//////////////////////////////// + +@implementation OpenGL_View{ + b32 gl_is_initialized; } - (id)init{ @@ -27,8 +41,8 @@ f64 mac_get_time_diff_sec(u64 begin, u64 end){ return nil; } - glIsInitialized = false; - [self initGL]; + gl_is_initialized = false; + [self init_gl]; return self; } @@ -43,13 +57,13 @@ f64 mac_get_time_diff_sec(u64 begin, u64 end){ [[self openGLContext] makeCurrentContext]; // NOTE(yuval): Setup vsync - GLint swapInt = 1; - printf("Using vsync: %d\n", swapInt); - [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + GLint swap_int = 1; + printf("Using vsync: %d\n", swap_int); + [[self openGLContext] setValues:&swap_int forParameter:NSOpenGLCPSwapInterval]; } - (void)awakeFromNib{ - [self initGL]; + [self init_gl]; } - (void)reshape{ @@ -59,8 +73,8 @@ f64 mac_get_time_diff_sec(u64 begin, u64 end){ [[self openGLContext] update]; } -- (void)initGL{ - if (glIsInitialized){ +- (void)init_gl{ + if (gl_is_initialized){ return; } @@ -92,65 +106,78 @@ f64 mac_get_time_diff_sec(u64 begin, u64 end){ [pixel_format release]; - glIsInitialized = true; + gl_is_initialized = true; } - (void)render:(Render_Target*)target{ - Assert(glIsInitialized); + Assert(gl_is_initialized); - u64 context_lock_begin = system_now_time(); CGLLockContext([[self openGLContext] CGLContextObj]); - u64 context_lock_end = system_now_time(); - printf("Context lock time: %fs\n", mac_get_time_diff_sec(context_lock_begin, context_lock_end)); - - u64 make_current_context_begin = system_now_time(); [[self openGLContext] makeCurrentContext]; - u64 make_current_context_end = system_now_time(); - printf("Make current context time: %fs\n", mac_get_time_diff_sec(make_current_context_begin, make_current_context_end)); - u64 gl_render_begin = system_now_time(); gl_render(target); - u64 gl_render_end = system_now_time(); - printf("GL render time: %fs\n", mac_get_time_diff_sec(gl_render_begin, gl_render_end)); - u64 gl_flush_buffer_begin = system_now_time(); [[self openGLContext] flushBuffer]; - u64 gl_flush_buffer_end = system_now_time(); - printf("GL flush buffer time: %fs\n", mac_get_time_diff_sec(gl_flush_buffer_begin, gl_flush_buffer_end)); - - u64 context_unlock_begin = system_now_time(); CGLUnlockContext([[self openGLContext] CGLContextObj]); - u64 context_unlock_end = system_now_time(); - printf("Context unlock time: %fs\n", mac_get_time_diff_sec(context_unlock_begin, context_unlock_end)); } @end -global OpenGLView *opengl_view; +//////////////////////////////// -function void -mac_gl_init(NSWindow *window){ - // NOTE(yuval): Create OpenGLView +function +mac_render_sig(mac_gl_render){ + printf("Rendering using OpenGL!\n"); + + Mac_OpenGL_Renderer *gl = (Mac_OpenGL_Renderer*)renderer; + [gl->opengl_view render:target]; +} + +function +mac_get_texture_sig(mac_gl_get_texture){ + u32 result = gl__get_texture(dim, texture_kind); + return(result); +} + +function +mac_fill_texture_sig(mac_gl_fill_texture){ + b32 result = gl__fill_texture(texture_kind, texture, p, dim, data); + return(result); +} + +function Mac_OpenGL_Renderer* +mac_gl__init(NSWindow *window, Render_Target *target){ + // NOTE(yuval): Create the Mac Renderer + Mac_OpenGL_Renderer *gl = (Mac_OpenGL_Renderer*)system_memory_allocate(sizeof(Mac_OpenGL_Renderer), + file_name_line_number_lit_u8); + gl->base.render = mac_gl_render; + gl->base.get_texture = mac_gl_get_texture; + gl->base.fill_texture = mac_gl_fill_texture; + + // NOTE(yuval): Create the OpenGL view NSView *content_view = [window contentView]; - opengl_view = [[OpenGLView alloc] init]; - [opengl_view setFrame:[content_view bounds]]; - [opengl_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [opengl_view setWantsBestResolutionOpenGLSurface:YES]; + gl->opengl_view = [[OpenGL_View alloc] init]; + [gl->opengl_view setFrame:[content_view bounds]]; + [gl->opengl_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [gl->opengl_view setWantsBestResolutionOpenGLSurface:YES]; // NOTE(yuval): Add the OpenGL view as a subview of the window - [content_view addSubview:opengl_view]; + [content_view addSubview:gl->opengl_view]; // NOTE(yuval): Load gl functions void *gl_image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY); #define GL_FUNC(f,R,P) ((f) = (f##_Function*)dlsym(gl_image, #f)); #include "mac_4ed_opengl_funcs.h" + + return(gl); } -function void -mac_gl_render(Render_Target* target){ - u64 begin_time = system_now_time(); - [opengl_view render:target]; - u64 end_time = system_now_time(); - printf("Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); -} \ No newline at end of file +// TODO(yuval): This function should be exported to a DLL +function +mac_load_renderer_sig(mac_load_opengl_renderer){ + printf("Initializing The OpenGL Renderer!\n"); + + Mac_Renderer *renderer = (Mac_Renderer*)mac_gl__init(window, target); + return(renderer); +} diff --git a/platform_mac/mac_4ed_renderer.h b/platform_mac/mac_4ed_renderer.h new file mode 100644 index 00000000..0d1d4caf --- /dev/null +++ b/platform_mac/mac_4ed_renderer.h @@ -0,0 +1,42 @@ +/* Mac Renderer Abstraction */ + +#if !defined(FRED_MAC_RENDERER_H) +#define FRED_MAC_RENDERER_H + +//////////////////////////////// + +// TODO(yuval): This should be refactored into a platform independent renderer + +struct Mac_Renderer; + +#define mac_render_sig(name) void name(Mac_Renderer *renderer, Render_Target *target) +typedef mac_render_sig(mac_render_type); + +#define mac_get_texture_sig(name) u32 name(Mac_Renderer *renderer, Vec3_i32 dim, Texture_Kind texture_kind) +typedef mac_get_texture_sig(mac_get_texture_type); + +#define mac_fill_texture_sig(name) b32 name(Mac_Renderer *renderer, Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void* data) +typedef mac_fill_texture_sig(mac_fill_texture_type); + +typedef i32 Mac_Renderer_Kind; +enum{ + MacRenderer_OpenGL, + MacRenderer_Metal +}; + +struct Mac_Renderer{ + mac_render_type *render; + + mac_get_texture_type *get_texture; + mac_fill_texture_type *fill_texture; +}; + +//////////////////////////////// + +// NOTE(yuval): This is the actual platform dependent function that each renderer implementation implements and should be exported into a DLL +#define mac_load_renderer_sig(name) Mac_Renderer* name(NSWindow *window, Render_Target *target) +typedef mac_load_renderer_sig(mac_load_renderer_type); + +//////////////////////////////// + +#endif \ No newline at end of file diff --git a/platform_mac/mac_4ed_renderer.mm b/platform_mac/mac_4ed_renderer.mm new file mode 100644 index 00000000..f1b18967 --- /dev/null +++ b/platform_mac/mac_4ed_renderer.mm @@ -0,0 +1,33 @@ +/* Mac Renderer Abstraction Implementation */ + +// TODO(yuval): This should NOT be included here once the renderer is exported to a DLL +#import "mac_4ed_opengl.mm" +#import "mac_4ed_metal.mm" + +function Mac_Renderer* +mac_init_renderer(Mac_Renderer_Kind kind, NSWindow *window, Render_Target *target){ + // TODO(yuval): Import renderer load function from a DLL instead of using a switch statement and a renderer kind. This would allow us to switch the renderer backend and implemented new backends with ease. + + Mac_Renderer *result = 0; + + switch (kind){ + case MacRenderer_OpenGL: + { + result = mac_load_opengl_renderer(window, target); + } break; + + case MacRenderer_Metal: + { + //result = mac_load_metal_renderer(window, target); + } break; + + default: InvalidPath; + } + + if (!result){ + mac_error_box("Unable to initialize the renderer!"); + } + + return result; +} + From 88c933acd2d3ca548bbc40057952f9ea2fd5a866 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 9 Jan 2020 02:36:37 +0200 Subject: [PATCH 49/67] Implemented the renderer abstraction for both OpenGL and Metal. --- metal/4ed_metal_render.mm | 2 - platform_mac/mac_4ed.mm | 8 +-- platform_mac/mac_4ed_metal.mm | 101 +++++++++++++++++++++---------- platform_mac/mac_4ed_opengl.mm | 43 +++++++------ platform_mac/mac_4ed_renderer.mm | 2 +- 5 files changed, 94 insertions(+), 62 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index d5126396..3209e53e 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -259,8 +259,6 @@ metal__make_buffer(u32 size, id device){ } - (void)drawInMTKView:(nonnull MTKView*)view{ - printf("Metal Renderer Draw!\n"); - [capture_scope beginScope]; // HACK(yuval): This is the best way I found to force valid width and height without drawing on the next drawing cycle (1 frame delay). diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 33f645f7..29465700 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -447,8 +447,6 @@ this only gets called for window creation and other extraordinary events. [NSApp terminate:nil]; } - //mac_gl_render(&target); - //mac_metal_render(&target); renderer->render(renderer, &target); mac_vars.first = false; @@ -663,7 +661,7 @@ main(int arg_count, char **args){ } // - // Window and GL View Initialization + // Window and Renderer Initialization // // NOTE(yuval): Create Window & Window Delegate @@ -707,9 +705,7 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - //mac_gl_init(mac_vars.window); - //mac_metal_init(mac_vars.window, &target); - renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_Metal, mac_vars.window, &target); mac_resize(w, h); diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index ba44930b..117eec2d 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -1,50 +1,85 @@ +/* Mac Metal layer for 4coder */ + #import "metal/4ed_metal_render.mm" -global Metal_Renderer *metal_renderer; -global MTKView *metal_view; +//////////////////////////////// -function void -mac_metal_init(NSWindow *window, Render_Target *target){ - // NOTE(yuval): Create Metal view - NSView *content_view = [window contentView]; +struct Mac_Metal{ + Mac_Renderer base; - metal_view = [[MTKView alloc] initWithFrame:[content_view bounds]]; - [metal_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [metal_view setPaused:YES]; - [metal_view setEnableSetNeedsDisplay:NO]; - [metal_view setSampleCount:4]; + Metal_Renderer *renderer; + MTKView *view; +}; + +//////////////////////////////// + +function +mac_render_sig(mac_metal__render){ + printf("Rendering using Metal!\n"); - metal_view.device = MTLCreateSystemDefaultDevice(); - - // NOTE(yuval): Add the Metal view as a subview of the window - [content_view addSubview:metal_view]; - - // NOTE(yuval): Create the Metal renderer and set it as the Metal view's delegate - metal_renderer = [[Metal_Renderer alloc] initWithMetalKitView:metal_view]; - metal_view.delegate = metal_renderer; + Mac_Metal *metal = (Mac_Metal*)renderer; + metal->renderer.target = target; + [metal->view draw]; } -function void -mac_metal_render(Render_Target* target){ - metal_renderer.target = target; - [metal_view draw]; -} - -function u32 -mac_metal_get_texture(Vec3_i32 dim, Texture_Kind texture_kind){ - u32 result = [metal_renderer get_texture_of_dim:dim +function +mac_get_texture_sig(mac_metal__get_texture){ + Mac_Metal *metal = (Mac_Metal*)renderer; + u32 result = [metal->renderer get_texture_of_dim:dim kind:texture_kind]; - return result; + return(result); } -function b32 -mac_metal_fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void *data){ - b32 result = [metal_renderer fill_texture:texture +function +mac_fill_texture_sig(mac_metal__fill_texture){ + Mac_Metal *metal = (Mac_Metal*)renderer; + b32 result = [metal->renderer fill_texture:texture kind:texture_kind pos:p dim:dim data:data]; - return result; + return(result); +} + +function Mac_Metal* +mac_metal__init(NSWindow *window, Render_Target *target){ + // NOTE(yuval): Create the Mac Metal rendere + Mac_Metal *metal = (Mac_Metal*)system_memory_allocate(sizeof(Mac_Metal), + file_name_line_number_lit_u8); + metal->base.render = mac_metal__render; + metal->base.get_texture = mac_metal__get_texture; + metal->base.fill_texture = mac_metal__fill_texture; + + // NOTE(yuval): Create the Metal view + NSView *content_view = [window contentView]; + + metal->view = [[MTKView alloc] initWithFrame:[content_view bounds]]; + [metal->view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [metal->view setPaused:YES]; + [metal->view setEnableSetNeedsDisplay:NO]; + [metal->view setSampleCount:4]; + + metal->view.device = MTLCreateSystemDefaultDevice(); + + // NOTE(yuval): Add the Metal view as a subview of the window + [content_view addSubview:metal->view]; + + // NOTE(yuval): Create the Metal renderer and set it as the Metal view's delegate + metal->renderer = [[Metal_Renderer alloc] initWithMetalKitView:metal->view]; + metal->view.delegate = metal->renderer; + + return(metal); +} + +//////////////////////////////// + +// TODO(yuval): This function should be exported to a DLL +function +mac_load_renderer_sig(mac_load_metal_renderer){ + printf("Loding The Metal Renderer!\n"); + + Mac_Renderer *renderer = (Mac_Renderer*)mac_metal__init(window, target); + return(renderer); } diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index 0a73c9c9..57dd3348 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -24,9 +24,10 @@ //////////////////////////////// -struct Mac_OpenGL_Renderer{ +struct Mac_OpenGL{ Mac_Renderer base; - OpenGL_View *opengl_view; + + OpenGL_View *view; }; //////////////////////////////// @@ -125,44 +126,44 @@ struct Mac_OpenGL_Renderer{ //////////////////////////////// function -mac_render_sig(mac_gl_render){ +mac_render_sig(mac_gl__render){ printf("Rendering using OpenGL!\n"); - Mac_OpenGL_Renderer *gl = (Mac_OpenGL_Renderer*)renderer; - [gl->opengl_view render:target]; + Mac_OpenGL *gl = (Mac_OpenGL*)renderer; + [gl->view render:target]; } function -mac_get_texture_sig(mac_gl_get_texture){ +mac_get_texture_sig(mac_gl__get_texture){ u32 result = gl__get_texture(dim, texture_kind); return(result); } function -mac_fill_texture_sig(mac_gl_fill_texture){ +mac_fill_texture_sig(mac_gl__fill_texture){ b32 result = gl__fill_texture(texture_kind, texture, p, dim, data); return(result); } -function Mac_OpenGL_Renderer* +function Mac_OpenGL* mac_gl__init(NSWindow *window, Render_Target *target){ - // NOTE(yuval): Create the Mac Renderer - Mac_OpenGL_Renderer *gl = (Mac_OpenGL_Renderer*)system_memory_allocate(sizeof(Mac_OpenGL_Renderer), - file_name_line_number_lit_u8); - gl->base.render = mac_gl_render; - gl->base.get_texture = mac_gl_get_texture; - gl->base.fill_texture = mac_gl_fill_texture; + // NOTE(yuval): Create the Mac OpenGL Renderer + Mac_OpenGL *gl = (Mac_OpenGL*)system_memory_allocate(sizeof(Mac_OpenGL), + file_name_line_number_lit_u8); + gl->base.render = mac_gl__render; + gl->base.get_texture = mac_gl__get_texture; + gl->base.fill_texture = mac_gl__fill_texture; // NOTE(yuval): Create the OpenGL view NSView *content_view = [window contentView]; - gl->opengl_view = [[OpenGL_View alloc] init]; - [gl->opengl_view setFrame:[content_view bounds]]; - [gl->opengl_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [gl->opengl_view setWantsBestResolutionOpenGLSurface:YES]; + gl->view = [[OpenGL_View alloc] init]; + [gl->view setFrame:[content_view bounds]]; + [gl->view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [gl->view setWantsBestResolutionOpenGLSurface:YES]; // NOTE(yuval): Add the OpenGL view as a subview of the window - [content_view addSubview:gl->opengl_view]; + [content_view addSubview:gl->view]; // NOTE(yuval): Load gl functions void *gl_image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY); @@ -173,10 +174,12 @@ mac_gl__init(NSWindow *window, Render_Target *target){ return(gl); } +//////////////////////////////// + // TODO(yuval): This function should be exported to a DLL function mac_load_renderer_sig(mac_load_opengl_renderer){ - printf("Initializing The OpenGL Renderer!\n"); + printf("Loding The OpenGL Renderer!\n"); Mac_Renderer *renderer = (Mac_Renderer*)mac_gl__init(window, target); return(renderer); diff --git a/platform_mac/mac_4ed_renderer.mm b/platform_mac/mac_4ed_renderer.mm index f1b18967..5b4e4015 100644 --- a/platform_mac/mac_4ed_renderer.mm +++ b/platform_mac/mac_4ed_renderer.mm @@ -18,7 +18,7 @@ mac_init_renderer(Mac_Renderer_Kind kind, NSWindow *window, Render_Target *targe case MacRenderer_Metal: { - //result = mac_load_metal_renderer(window, target); + result = mac_load_metal_renderer(window, target); } break; default: InvalidPath; From 533819c7d4550f6f716b5ce4fa3e5b43c0a31c63 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 9 Jan 2020 20:52:39 +0200 Subject: [PATCH 50/67] Added mouse input. --- platform_mac/mac_4ed.mm | 63 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 29465700..62dc3390 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -128,6 +128,7 @@ struct Mac_Input_Chunk{ @interface FCoderView : NSView - (void)requestDisplay; +- (void)process_mouse_move_event:(NSEvent*)event; @end //////////////////////////////// @@ -394,6 +395,9 @@ mac_resize(NSWindow *window){ } - (void)drawRect:(NSRect)bounds{ + // NOTE(yuval): Read comment in win32_4ed.cpp's main loop + system_mutex_acquire(mac_vars.global_frame_mutex); + /* NOTE(yuval): Force the graphics context to clear to black so we don't get a flash of white until the app is ready to draw. In practice on modern macOS, this only gets called for window creation and other extraordinary events. @@ -401,9 +405,6 @@ this only gets called for window creation and other extraordinary events. [[NSColor blackColor] setFill]; NSRectFill(bounds); - // NOTE(yuval): Read comment in win32_4ed.cpp's main loop - system_mutex_release(mac_vars.global_frame_mutex); - // NOTE(yuval): Prepare the Frame Input Mac_Input_Chunk input_chunk = mac_vars.input_chunk; Application_Step_Input input = {}; @@ -452,6 +453,8 @@ this only gets called for window creation and other extraordinary events. mac_vars.first = false; linalloc_clear(mac_vars.frame_arena); + + system_mutex_release(mac_vars.global_frame_mutex); } - (BOOL)acceptsFirstResponder{ @@ -488,9 +491,50 @@ this only gets called for window creation and other extraordinary events. } - (void)mouseMoved:(NSEvent*)event{ + [self process_mouse_move_event:event]; +} + +- (void)mouseDragged:(NSEvent*)event{ + [self process_mouse_move_event:event]; +} + +- (void)scrollWheel:(NSEvent *)event{ + float dx = event.scrollingDeltaX; + float dy = event.scrollingDeltaY; + + mac_vars.input_chunk.trans.mouse_wheel = -((int32_t)dy); + + system_signal_step(0); } - (void)mouseDown:(NSEvent*)event{ + mac_vars.input_chunk.trans.mouse_l_press = true; + mac_vars.input_chunk.pers.mouse_l = true; + + system_signal_step(0); +} + +- (void)mouseUp:(NSEvent*)event{ + mac_vars.input_chunk.trans.mouse_l_release = true; + mac_vars.input_chunk.pers.mouse_l = false; + + system_signal_step(0); +} + +- (void)rightMouseDown:(NSEvent*)event{ + [super rightMouseDown:event]; + + mac_vars.input_chunk.trans.mouse_r_press = true; + mac_vars.input_chunk.pers.mouse_r = true; + + system_signal_step(0); +} + +- (void)rightMouseUp:(NSEvent*)event{ + mac_vars.input_chunk.trans.mouse_r_release = true; + mac_vars.input_chunk.pers.mouse_r = false; + + system_signal_step(0); } - (void)requestDisplay{ @@ -498,6 +542,16 @@ this only gets called for window creation and other extraordinary events. NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; } + +- (void)process_mouse_move_event:(NSEvent*)event{ + NSPoint location = [event locationInWindow]; + Vec2_i32 new_m = V2i32(location.x, mac_vars.height - location.y); + if (new_m != mac_vars.input_chunk.pers.mouse){ + mac_vars.input_chunk.pers.mouse = new_m; + } + + system_signal_step(0); +} @end //////////////////////////////// @@ -728,13 +782,12 @@ main(int arg_count, char **args){ } // - // Main loop + // Start Main Loop // mac_vars.first = true; mac_vars.global_frame_mutex = system_mutex_make(); - system_mutex_acquire(mac_vars.global_frame_mutex); mac_vars.timer_start = system_now_time(); From 520ab3dd27875d3302b4030f80808ba62314eae4 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 10 Jan 2020 01:04:55 +0200 Subject: [PATCH 51/67] Fixed animations. --- platform_mac/mac_4ed.mm | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 62dc3390..31ff96ef 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -430,6 +430,8 @@ this only gets called for window creation and other extraordinary events. block_zero_struct(&mac_vars.input_chunk.trans); + printf("Step scroll wheel: %d\n", input.mouse.wheel); + // NOTE(yuval): See comment in win32_4ed.cpp's main loop if (mac_vars.send_exit_signal){ input.trying_to_kill = true; @@ -448,7 +450,16 @@ this only gets called for window creation and other extraordinary events. [NSApp terminate:nil]; } + // NOTE(yuval): Render + u64 begin_render = system_now_time(); renderer->render(renderer, &target); + u64 end_render = system_now_time(); + printf("Render Time: %fs\n\n", (end_render - begin_render) / 1000000.0f); + + // NOTE(yuval): Schedule another step if needed + if (result.animating){ + system_signal_step(0); + } mac_vars.first = false; @@ -480,10 +491,9 @@ this only gets called for window creation and other extraordinary events. String_Const_u32 str_32 = SCu32(&character_code, 1); String_Const_u8 str_8 = string_u8_from_string_u32(mac_vars.frame_arena, str_32).string; - Input_Event event = {}; - event.kind = InputEventKind_TextInsert; - event.text.string = str_8; - push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list, &event); + Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); + event->kind = InputEventKind_TextInsert; + event->text.string = str_8; system_signal_step(0); } @@ -502,7 +512,14 @@ this only gets called for window creation and other extraordinary events. float dx = event.scrollingDeltaX; float dy = event.scrollingDeltaY; - mac_vars.input_chunk.trans.mouse_wheel = -((int32_t)dy); + printf("Mouse scroll - dx:%f dy:%f\n", dx, dy); + + //mac_vars.input_chunk.trans.mouse_wheel = -((int32_t)dy); + i8 scroll_speed = 100; + if (dy > 0){ + scroll_speed *= -1; + } + mac_vars.input_chunk.trans.mouse_wheel = scroll_speed; system_signal_step(0); } @@ -759,7 +776,7 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - renderer = mac_init_renderer(MacRenderer_Metal, mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); mac_resize(w, h); From 13970744b485aacc9ee1ba118f7d347cf64fc0b0 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 10 Jan 2020 18:57:08 +0200 Subject: [PATCH 52/67] Fixed blending bug in the Metal renderer where the destination alpha was not updated which caused problems while rendering the program in full screen mode. --- metal/4ed_metal_render.mm | 50 ++++++++++++++++++++++++----------- platform_mac/mac_4ed.mm | 31 ++++++++++++++-------- platform_mac/mac_4ed_metal.mm | 2 +- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 3209e53e..c7452e02 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -25,9 +25,12 @@ struct Metal_Buffer{ @interface Metal_Renderer : NSObject @property (nonatomic) Render_Target *target; -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtkView; +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtkView target:(Render_Target*)target; + - (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind; - (b32)fill_texture:(u32)texture kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data; +- (void)bind_texture:(u32)handle encoder:(id)render_encoder; + - (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size; - (void)add_reusable_buffer:(Metal_Buffer*)buffer; @end @@ -127,8 +130,8 @@ sd = (abs(sd + in.half_thickness) - in.half_thickness); float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd)); shape_value *= has_thickness; -// TOOD(yuval): Add sample_value to alpha float4 out_color = float4(in.color.xyz, in.color.a * (sample_value + shape_value)); +//float4 out_color = float4(1, 1, 1, shape_value); return(out_color); } )"; @@ -165,7 +168,7 @@ metal__make_buffer(u32 size, id device){ u32 next_texture_handle_index; } -- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{ +- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view target:(Render_Target*)target{ self = [super init]; if (self == nil){ return(nil); @@ -226,8 +229,8 @@ metal__make_buffer(u32 size, id device){ pipeline_state_descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; pipeline_state_descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; pipeline_state_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - /*pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; - pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;*/ + pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne; + pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; pipeline_state = [device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor error:&error]; @@ -246,6 +249,16 @@ metal__make_buffer(u32 size, id device){ textures = (Metal_Texture*)system_memory_allocate(metal__max_textures * sizeof(Metal_Texture), file_name_line_number_lit_u8); next_texture_handle_index = 0; + // NOTE(yuval): Create the fallback texture + target->fallback_texture_id = [self get_texture_of_dim:V3i32(2, 2, 1) + kind:TextureKind_Mono]; + u8 white_block[] = {0xFF, 0xFF, 0xFF, 0xFF}; + [self fill_texture:target->fallback_texture_id + kind:TextureKind_Mono + pos:V3i32(0, 0, 0) + dim:V3i32(2, 2, 1) + data:white_block]; + // NOTE(yuval): Create a capture scope for gpu frame capture capture_scope = [[MTLCaptureManager sharedCaptureManager] newCaptureScopeWithDevice:device]; @@ -324,10 +337,9 @@ metal__make_buffer(u32 size, id device){ atIndex:1]; u32 buffer_offset = 0; - i32 count = 0; for (Render_Group *group = _target->group_first; group; - group = group->next, ++count){ + group = group->next){ // NOTE(yuval): Set scissor rect { Rect_i32 box = Ri32(group->clip_box); @@ -349,19 +361,17 @@ metal__make_buffer(u32 size, id device){ i32 vertex_count = group->vertex_list.vertex_count; if (vertex_count > 0){ - // TODO(yuval): Bind a texture + // NOTE(yuval): Bind a texture { Face* face = font_set_face_from_id(font_set, group->face_id); if (face != 0){ - // TODO(yuval): Bind face texture - u32 texture_handle = face->texture; - Metal_Texture texture = textures[texture_handle]; - if (texture != 0){ - [render_encoder setFragmentTexture:texture - atIndex:0]; - } + // NOTE(yuval): Bind face texture + [self bind_texture:face->texture + encoder:render_encoder]; } else{ - // TODO(yuval): Bind default texture + // NOTE(yuval): Bind fallback texture + [self bind_texture:_target->fallback_texture_id + encoder:render_encoder]; } } @@ -458,6 +468,14 @@ metal__make_buffer(u32 size, id device){ return result; } +- (void)bind_texture:(u32)handle encoder:(id)render_encoder{ + Metal_Texture texture = textures[handle]; + if (texture != 0){ + [render_encoder setFragmentTexture:texture + atIndex:0]; + } +} + - (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size{ // NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::dequeueReusableBufferOfLength in imgui_impl_metal.mm diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 31ff96ef..f8ad5a20 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -360,21 +360,35 @@ mac_resize(NSWindow *window){ @end @implementation FCoderWindowDelegate -- (BOOL)windowShouldClose:(id)sender { +- (BOOL)windowShouldClose:(id)sender{ mac_vars.input_chunk.trans.trying_to_kill = true; system_signal_step(0); return(NO); } -- (void)windowDidResize:(NSNotification*)notification { +- (void)windowDidResize:(NSNotification*)notification{ mac_resize(mac_vars.window); } -- (void)windowDidMiniaturize:(NSNotification*)notification { +- (void)windowDidMiniaturize:(NSNotification*)notification{ } -- (void)windowDidDeminiaturize:(NSNotification*)notification { +- (void)windowDidDeminiaturize:(NSNotification*)notification{ +} + +- (void)windowDidBecomeKey:(NSNotification *)notification{ + printf("Focus\n"); + system_signal_step(0); +} + +- (void)windowDidResignKey:(NSNotification *)notification{ + printf("Lost Focus\n"); + system_signal_step(0); +} + +- (void)windowDidEnterFullScreen:(NSNotification *)notification{ + system_signal_step(0); } @end @@ -430,8 +444,6 @@ this only gets called for window creation and other extraordinary events. block_zero_struct(&mac_vars.input_chunk.trans); - printf("Step scroll wheel: %d\n", input.mouse.wheel); - // NOTE(yuval): See comment in win32_4ed.cpp's main loop if (mac_vars.send_exit_signal){ input.trying_to_kill = true; @@ -512,9 +524,6 @@ this only gets called for window creation and other extraordinary events. float dx = event.scrollingDeltaX; float dy = event.scrollingDeltaY; - printf("Mouse scroll - dx:%f dy:%f\n", dx, dy); - - //mac_vars.input_chunk.trans.mouse_wheel = -((int32_t)dy); i8 scroll_speed = 100; if (dy > 0){ scroll_speed *= -1; @@ -567,7 +576,7 @@ this only gets called for window creation and other extraordinary events. mac_vars.input_chunk.pers.mouse = new_m; } - system_signal_step(0); + //system_signal_step(0); } @end @@ -776,7 +785,7 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_Metal, mac_vars.window, &target); mac_resize(w, h); diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 117eec2d..60b754d9 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -67,7 +67,7 @@ mac_metal__init(NSWindow *window, Render_Target *target){ [content_view addSubview:metal->view]; // NOTE(yuval): Create the Metal renderer and set it as the Metal view's delegate - metal->renderer = [[Metal_Renderer alloc] initWithMetalKitView:metal->view]; + metal->renderer = [[Metal_Renderer alloc] initWithMetalKitView:metal->view target:target]; metal->view.delegate = metal->renderer; return(metal); From 8e4ec5215f94a6a4544c3bbeb1ec1421d93520f7 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sat, 11 Jan 2020 22:19:43 +0200 Subject: [PATCH 53/67] Added KeyStoke and KeyRelease events and fixed TextInput events. --- metal/4ed_metal_render.mm | 6 +- platform_mac/mac_4ed.mm | 245 ++++++++++++++++++++++++++---- platform_mac/mac_4ed_functions.mm | 4 +- platform_mac/mac_4ed_metal.mm | 1 - 4 files changed, 219 insertions(+), 37 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index c7452e02..6f8b1a20 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -174,6 +174,8 @@ metal__make_buffer(u32 size, id device){ return(nil); } + _target = target; + NSError *error = nil; device = mtk_view.device; @@ -250,10 +252,10 @@ metal__make_buffer(u32 size, id device){ next_texture_handle_index = 0; // NOTE(yuval): Create the fallback texture - target->fallback_texture_id = [self get_texture_of_dim:V3i32(2, 2, 1) + _target->fallback_texture_id = [self get_texture_of_dim:V3i32(2, 2, 1) kind:TextureKind_Mono]; u8 white_block[] = {0xFF, 0xFF, 0xFF, 0xFF}; - [self fill_texture:target->fallback_texture_id + [self fill_texture:_target->fallback_texture_id kind:TextureKind_Mono pos:V3i32(0, 0, 0) dim:V3i32(2, 2, 1) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index f8ad5a20..c5dc43fb 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -55,6 +55,7 @@ #import #include // NOTE(yuval): Used for proc_pidpath +#include // NOTE(yuval): Used for virtual key codes #include // NOTE(yuval): Used for mach_absolute_time, mach_timebase_info, mach_timebase_info_data_t #include // NOTE(yuval): Used for opendir, readdir @@ -120,14 +121,15 @@ struct Mac_Input_Chunk{ //////////////////////////////// -@interface FCoderAppDelegate : NSObject +@interface FCoder_App_Delegate : NSObject @end -@interface FCoderWindowDelegate : NSObject +@interface FCoder_Window_Delegate : NSObject @end -@interface FCoderView : NSView -- (void)requestDisplay; +@interface FCoder_View : NSView +- (void)request_display; +- (void)process_keyboard_event:(NSEvent*)event; - (void)process_mouse_move_event:(NSEvent*)event; @end @@ -166,7 +168,10 @@ struct Mac_Vars { Thread_Context *tctx; Arena *frame_arena; + Input_Event *active_key_stroke; + Input_Event *active_text_input; Mac_Input_Chunk input_chunk; + b8 lctrl_lalt_is_altgr; b8 full_screen; b8 do_toggle; @@ -180,7 +185,7 @@ struct Mac_Vars { String_Const_u8 clipboard_contents; NSWindow *window; - FCoderView *view; + FCoder_View *view; f32 screen_scale_factor; mach_timebase_info_data_t timebase_info; @@ -300,11 +305,101 @@ mac_error_box(char *msg, b32 shutdown = true){ //////////////////////////////// -global Key_Code keycode_lookup_table[255]; +global Key_Code keycode_lookup_table[255] = {}; function void -mac_key_code_init(void){ +mac_keycode_init(void){ + keycode_lookup_table[kVK_ANSI_A] = KeyCode_A; + keycode_lookup_table[kVK_ANSI_B] = KeyCode_B; + keycode_lookup_table[kVK_ANSI_C] = KeyCode_C; + keycode_lookup_table[kVK_ANSI_D] = KeyCode_D; + keycode_lookup_table[kVK_ANSI_E] = KeyCode_E; + keycode_lookup_table[kVK_ANSI_F] = KeyCode_F; + keycode_lookup_table[kVK_ANSI_G] = KeyCode_G; + keycode_lookup_table[kVK_ANSI_H] = KeyCode_H; + keycode_lookup_table[kVK_ANSI_I] = KeyCode_I; + keycode_lookup_table[kVK_ANSI_J] = KeyCode_J; + keycode_lookup_table[kVK_ANSI_K] = KeyCode_K; + keycode_lookup_table[kVK_ANSI_L] = KeyCode_L; + keycode_lookup_table[kVK_ANSI_M] = KeyCode_M; + keycode_lookup_table[kVK_ANSI_N] = KeyCode_N; + keycode_lookup_table[kVK_ANSI_O] = KeyCode_O; + keycode_lookup_table[kVK_ANSI_P] = KeyCode_P; + keycode_lookup_table[kVK_ANSI_Q] = KeyCode_Q; + keycode_lookup_table[kVK_ANSI_R] = KeyCode_R; + keycode_lookup_table[kVK_ANSI_S] = KeyCode_S; + keycode_lookup_table[kVK_ANSI_T] = KeyCode_T; + keycode_lookup_table[kVK_ANSI_U] = KeyCode_U; + keycode_lookup_table[kVK_ANSI_V] = KeyCode_V; + keycode_lookup_table[kVK_ANSI_W] = KeyCode_W; + keycode_lookup_table[kVK_ANSI_X] = KeyCode_X; + keycode_lookup_table[kVK_ANSI_Y] = KeyCode_Y; + keycode_lookup_table[kVK_ANSI_Z] = KeyCode_Z; + keycode_lookup_table[kVK_ANSI_0] = KeyCode_0; + keycode_lookup_table[kVK_ANSI_1] = KeyCode_1; + keycode_lookup_table[kVK_ANSI_2] = KeyCode_2; + keycode_lookup_table[kVK_ANSI_3] = KeyCode_3; + keycode_lookup_table[kVK_ANSI_4] = KeyCode_4; + keycode_lookup_table[kVK_ANSI_5] = KeyCode_5; + keycode_lookup_table[kVK_ANSI_6] = KeyCode_6; + keycode_lookup_table[kVK_ANSI_7] = KeyCode_7; + keycode_lookup_table[kVK_ANSI_8] = KeyCode_8; + keycode_lookup_table[kVK_ANSI_9] = KeyCode_9; + + keycode_lookup_table[kVK_Space] = KeyCode_Space; + keycode_lookup_table[kVK_ANSI_Grave] = KeyCode_Tick; + keycode_lookup_table[kVK_ANSI_Minus] = KeyCode_Minus; + keycode_lookup_table[kVK_ANSI_LeftBracket] = KeyCode_LeftBracket; + keycode_lookup_table[kVK_ANSI_RightBracket] = KeyCode_RightBracket; + keycode_lookup_table[kVK_ANSI_Semicolon] = KeyCode_Semicolon; + keycode_lookup_table[kVK_ANSI_Quote] = KeyCode_Quote; + keycode_lookup_table[kVK_ANSI_Comma] = KeyCode_Comma; + keycode_lookup_table[kVK_ANSI_Period] = KeyCode_Period; + keycode_lookup_table[kVK_ANSI_Slash] = KeyCode_ForwardSlash; + keycode_lookup_table[kVK_ANSI_Backslash] = KeyCode_BackwardSlash; + + keycode_lookup_table[kVK_Tab] = KeyCode_Tab; + // NOTE(yuval): No Pause key on macOS! + keycode_lookup_table[kVK_Escape] = KeyCode_Escape; + + keycode_lookup_table[kVK_UpArrow] = KeyCode_Up; + keycode_lookup_table[kVK_DownArrow] = KeyCode_Down; + keycode_lookup_table[kVK_LeftArrow] = KeyCode_Left; + keycode_lookup_table[kVK_RightArrow] = KeyCode_Right; + + keycode_lookup_table[kVK_Delete] = KeyCode_Backspace; + keycode_lookup_table[kVK_Return] = KeyCode_Return; + + keycode_lookup_table[kVK_ForwardDelete] = KeyCode_Delete; + //keycode_lookup_table[] = KeyCode_Insert; // TODO(yuval): Figure how to get keyDown events for the insert key + keycode_lookup_table[kVK_Home] = KeyCode_Home; + keycode_lookup_table[kVK_End] = KeyCode_End; + keycode_lookup_table[kVK_PageUp] = KeyCode_PageUp; + keycode_lookup_table[kVK_PageDown] = KeyCode_PageDown; + + keycode_lookup_table[kVK_CapsLock] = KeyCode_CapsLock; + keycode_lookup_table[kVK_ANSI_KeypadClear] = KeyCode_NumLock; + // NOTE(yuval): No Scroll Lock key on macOS! + keycode_lookup_table[0x6E] = KeyCode_Menu; + + keycode_lookup_table[kVK_F1] = KeyCode_F1; + keycode_lookup_table[kVK_F2] = KeyCode_F2; + keycode_lookup_table[kVK_F3] = KeyCode_F3; + keycode_lookup_table[kVK_F4] = KeyCode_F4; + keycode_lookup_table[kVK_F5] = KeyCode_F5; + keycode_lookup_table[kVK_F6] = KeyCode_F6; + keycode_lookup_table[kVK_F7] = KeyCode_F7; + keycode_lookup_table[kVK_F8] = KeyCode_F8; + keycode_lookup_table[kVK_F9] = KeyCode_F9; + + keycode_lookup_table[kVK_F10] = KeyCode_F10; + keycode_lookup_table[kVK_F11] = KeyCode_F11; + keycode_lookup_table[kVK_F12] = KeyCode_F12; + keycode_lookup_table[kVK_F13] = KeyCode_F13; + keycode_lookup_table[kVK_F14] = KeyCode_F14; + keycode_lookup_table[kVK_F15] = KeyCode_F15; + keycode_lookup_table[kVK_F16] = KeyCode_F16; } //////////////////////////////// @@ -347,7 +442,7 @@ mac_resize(NSWindow *window){ //////////////////////////////// -@implementation FCoderAppDelegate +@implementation FCoder_App_Delegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -359,7 +454,7 @@ mac_resize(NSWindow *window){ } @end -@implementation FCoderWindowDelegate +@implementation FCoder_Window_Delegate - (BOOL)windowShouldClose:(id)sender{ mac_vars.input_chunk.trans.trying_to_kill = true; system_signal_step(0); @@ -392,7 +487,7 @@ mac_resize(NSWindow *window){ } @end -@implementation FCoderView +@implementation FCoder_View - (id)init{ self = [super init]; return(self); @@ -493,23 +588,17 @@ this only gets called for window creation and other extraordinary events. } - (void)keyDown:(NSEvent*)event{ - NSString *characters = [event characters]; - if ([characters length] != 0) { - u32 character_code = [characters characterAtIndex:0]; - - // NOTE(yuval): Control characters generate character_codes < 32 - if (character_code > 31) { - // TODO(yuval): This is actually in utf16!!! - String_Const_u32 str_32 = SCu32(&character_code, 1); - String_Const_u8 str_8 = string_u8_from_string_u32(mac_vars.frame_arena, str_32).string; - - Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); - event->kind = InputEventKind_TextInsert; - event->text.string = str_8; - - system_signal_step(0); - } - } + printf("Key Down: %#X\n", [event keyCode]); + [self process_keyboard_event:event]; +} + +- (void)keyUp:(NSEvent*)event{ + printf("Key Up: %#X\n", [event keyCode]); + [self process_keyboard_event:event]; +} + +- (void)flagsChanged:(NSEvent *)event{ + NSEventModifierFlags flags = [event modifierFlags]; } - (void)mouseMoved:(NSEvent*)event{ @@ -563,12 +652,102 @@ this only gets called for window creation and other extraordinary events. system_signal_step(0); } -- (void)requestDisplay{ +- (void)request_display{ CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; } +- (void)process_keyboard_event:(NSEvent*)event{ + b8 release = ([event type] == NSEventTypeKeyUp); + b8 down = !release; + + Input_Modifier_Set_Fixed *mods = &mac_vars.input_chunk.pers.modifiers; + + // NOTE(yuval): Set control modifiers + { + Control_Keys *controls = &mac_vars.input_chunk.pers.controls; + b8 ctrl = (controls->r_ctrl || (controls->l_ctrl && !controls->r_alt)); + b8 alt = (controls->l_alt || (controls->r_alt && !controls->l_ctrl)); + if (mac_vars.lctrl_lalt_is_altgr && controls->l_alt && controls->l_ctrl){ + ctrl = false; + alt = false; + } + + set_modifier(mods, KeyCode_Control, ctrl); + set_modifier(mods, KeyCode_Alt, alt); + } + + // NOTE(yuval): Process KeyStroke / KeyRelease event + { + u16 event_key_code = [event keyCode]; + Key_Code key = keycode_lookup_table[(u8)event_key_code]; + if (down){ + if (key != 0){ + add_modifier(mods, key); + + Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); + event->kind = InputEventKind_KeyStroke; + event->key.code = key; + event->key.modifiers = copy_modifier_set(mac_vars.frame_arena, mods); + + mac_vars.active_key_stroke = event; + + system_signal_step(0); + } + } else{ + mac_vars.active_key_stroke = 0; + mac_vars.active_text_input = 0; + + if (key != 0){ + Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); + event->kind = InputEventKind_KeyRelease; + event->key.code = key; + event->key.modifiers = copy_modifier_set(mac_vars.frame_arena, mods); + + remove_modifier(mods, key); + } + + system_signal_step(0); + } + } + + // NOTE(yuval): Process TextInput event + { + if (down){ + NSString *characters = [event characters]; + if ([characters length] > 0){ + // NOTE(yuval): Get the first utf-16 character + u32 c = [characters characterAtIndex:0]; + if (c == '\r'){ + c = '\n'; + } + + // NOTE(yuval): Check for a valid text input + if ((c > 127) || ((' ' <= c) && (c <= '~')) || (c == '\t') || (c == '\n') || (c == '\r')){ + String_Const_u16 str_16 = SCu16((u16*)&c, 1); + String_Const_u8 str_8 = string_u8_from_string_u16(mac_vars.frame_arena, str_16).string; + + Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); + event->kind = InputEventKind_TextInsert; + event->text.string = str_8; + event->text.next_text = 0; + event->text.blocked = false; + if (mac_vars.active_text_input){ + mac_vars.active_text_input->text.next_text = event; + } else if (mac_vars.active_key_stroke){ + mac_vars.active_key_stroke->key.first_dependent_text = event; + } + + mac_vars.active_text_input = event; + + system_signal_step(0); + } + } + } + } +} + - (void)process_mouse_move_event:(NSEvent*)event{ NSPoint location = [event locationInWindow]; Vec2_i32 new_m = V2i32(location.x, mac_vars.height - location.y); @@ -576,7 +755,7 @@ this only gets called for window creation and other extraordinary events. mac_vars.input_chunk.pers.mouse = new_m; } - //system_signal_step(0); + system_signal_step(0); } @end @@ -591,7 +770,7 @@ main(int arg_count, char **args){ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - FCoderAppDelegate *app_delegate = [[FCoderAppDelegate alloc] init]; + FCoder_App_Delegate *app_delegate = [[FCoder_App_Delegate alloc] init]; [NSApp setDelegate:app_delegate]; pthread_mutex_init(&memory_tracker_mutex, 0); @@ -765,7 +944,7 @@ main(int arg_count, char **args){ backing:NSBackingStoreBuffered defer:NO]; - FCoderWindowDelegate *window_delegate = [[FCoderWindowDelegate alloc] init]; + FCoder_Window_Delegate *window_delegate = [[FCoder_Window_Delegate alloc] init]; [mac_vars.window setDelegate:window_delegate]; [mac_vars.window setMinSize:NSMakeSize(100, 100)]; @@ -776,7 +955,7 @@ main(int arg_count, char **args){ NSView* content_view = [mac_vars.window contentView]; // NOTE(yuval): Create the 4coder view - mac_vars.view = [[FCoderView alloc] init]; + mac_vars.view = [[FCoder_View alloc] init]; [mac_vars.view setFrame:[content_view bounds]]; [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; @@ -793,6 +972,8 @@ main(int arg_count, char **args){ // TODO(yuval): Misc System Initializations // + mac_keycode_init(); + // NOTE(yuval): Get the timebase info mach_timebase_info(&mac_vars.timebase_info); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index c1c37bae..b62b4bb3 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -399,7 +399,7 @@ system_wake_up_timer_set_sig(){ f64 time_seconds = ((f64)time_milliseconds / 1000.0); object->timer = [NSTimer scheduledTimerWithTimeInterval:time_seconds target:mac_vars.view - selector:@selector(requestDisplay) + selector:@selector(request_display) userInfo:nil repeats:NO]; } } @@ -408,7 +408,7 @@ function system_signal_step_sig(){ [NSTimer scheduledTimerWithTimeInterval:0.0 target:mac_vars.view - selector:@selector(requestDisplay) + selector:@selector(request_display) userInfo:nil repeats:NO]; } diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 60b754d9..34fbbeca 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -18,7 +18,6 @@ mac_render_sig(mac_metal__render){ printf("Rendering using Metal!\n"); Mac_Metal *metal = (Mac_Metal*)renderer; - metal->renderer.target = target; [metal->view draw]; } From 7f00ead99c7b9be31dfcb7093a0d3325d68aabf3 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sat, 11 Jan 2020 23:12:41 +0200 Subject: [PATCH 54/67] Added support for control keys. --- platform_mac/mac_4ed.mm | 139 ++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 42 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index c5dc43fb..ec191dab 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -80,6 +80,10 @@ struct Control_Keys{ b8 r_ctrl; b8 l_alt; b8 r_alt; + b8 l_shift; + b8 r_shift; + b8 l_command; + b8 r_command; }; struct Mac_Input_Chunk_Transient{ @@ -129,7 +133,7 @@ struct Mac_Input_Chunk{ @interface FCoder_View : NSView - (void)request_display; -- (void)process_keyboard_event:(NSEvent*)event; +- (void)process_keyboard_event:(NSEvent*)event down:(b8)down; - (void)process_mouse_move_event:(NSEvent*)event; @end @@ -305,6 +309,8 @@ mac_error_box(char *msg, b32 shutdown = true){ //////////////////////////////// +global_const u8 kVK_Menu = 0x6E; + global Key_Code keycode_lookup_table[255] = {}; function void @@ -381,7 +387,19 @@ mac_keycode_init(void){ keycode_lookup_table[kVK_CapsLock] = KeyCode_CapsLock; keycode_lookup_table[kVK_ANSI_KeypadClear] = KeyCode_NumLock; // NOTE(yuval): No Scroll Lock key on macOS! - keycode_lookup_table[0x6E] = KeyCode_Menu; + keycode_lookup_table[kVK_Menu] = KeyCode_Menu; + + keycode_lookup_table[kVK_Shift] = KeyCode_Shift; + keycode_lookup_table[kVK_RightShift] = KeyCode_Shift; + + keycode_lookup_table[kVK_Control] = KeyCode_Control; + keycode_lookup_table[kVK_RightControl] = KeyCode_Control; + + keycode_lookup_table[kVK_Option] = KeyCode_Alt; + keycode_lookup_table[kVK_RightOption] = KeyCode_Alt; + + keycode_lookup_table[kVK_Command] = KeyCode_Command; + keycode_lookup_table[kVK_RightCommand] = KeyCode_Command; // NOTE(yuval): Right Command keycode_lookup_table[kVK_F1] = KeyCode_F1; keycode_lookup_table[kVK_F2] = KeyCode_F2; @@ -589,16 +607,83 @@ this only gets called for window creation and other extraordinary events. - (void)keyDown:(NSEvent*)event{ printf("Key Down: %#X\n", [event keyCode]); - [self process_keyboard_event:event]; + + // NOTE(yuval): Process keyboard event + [self process_keyboard_event:event down:true]; + + // NOTE(yuval): Process TextInsert event + { + NSString *characters = [event characters]; + if ([characters length] > 0){ + // NOTE(yuval): Get the first utf-16 character + u32 c = [characters characterAtIndex:0]; + if (c == '\r'){ + c = '\n'; + } + + // NOTE(yuval): Check for a valid text input + if ((c > 127) || ((' ' <= c) && (c <= '~')) || (c == '\t') || (c == '\n') || (c == '\r')){ + String_Const_u16 str_16 = SCu16((u16*)&c, 1); + String_Const_u8 str_8 = string_u8_from_string_u16(mac_vars.frame_arena, str_16).string; + + Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); + event->kind = InputEventKind_TextInsert; + event->text.string = str_8; + event->text.next_text = 0; + event->text.blocked = false; + if (mac_vars.active_text_input){ + mac_vars.active_text_input->text.next_text = event; + } else if (mac_vars.active_key_stroke){ + mac_vars.active_key_stroke->key.first_dependent_text = event; + } + + mac_vars.active_text_input = event; + + system_signal_step(0); + } + } + } } - (void)keyUp:(NSEvent*)event{ printf("Key Up: %#X\n", [event keyCode]); - [self process_keyboard_event:event]; + [self process_keyboard_event:event down:false]; } - (void)flagsChanged:(NSEvent *)event{ NSEventModifierFlags flags = [event modifierFlags]; + b8 ctrl_pressed = ((flags & NSEventModifierFlagControl) != 0); + b8 alt_pressed = ((flags & NSEventModifierFlagOption) != 0); + b8 shift_pressed = ((flags & NSEventModifierFlagShift) != 0); + b8 command_pressed = ((flags & NSEventModifierFlagCommand) != 0); + + Control_Keys *controls = &mac_vars.input_chunk.pers.controls; + u16 event_key_code = [event keyCode]; + if (event_key_code == kVK_Control){ + controls->l_ctrl = ctrl_pressed; + [self process_keyboard_event:event down:ctrl_pressed]; + } else if (event_key_code == kVK_RightControl){ + controls->r_ctrl = ctrl_pressed; + [self process_keyboard_event:event down:ctrl_pressed]; + } else if (event_key_code == kVK_Option){ + controls->l_alt = alt_pressed; + [self process_keyboard_event:event down:alt_pressed]; + } else if (event_key_code == kVK_RightOption){ + controls->r_alt = alt_pressed; + [self process_keyboard_event:event down:alt_pressed]; + } else if (event_key_code == kVK_Shift){ + controls->l_shift = shift_pressed; + [self process_keyboard_event:event down:shift_pressed]; + } else if (event_key_code == kVK_RightShift){ + controls->r_shift = shift_pressed; + [self process_keyboard_event:event down:shift_pressed]; + } else if (event_key_code == kVK_Command){ + controls->l_command = command_pressed; + [self process_keyboard_event:event down:command_pressed]; + } else if (event_key_code == kVK_RightCommand){ + controls->r_command = command_pressed; + [self process_keyboard_event:event down:command_pressed]; + } } - (void)mouseMoved:(NSEvent*)event{ @@ -658,15 +743,15 @@ this only gets called for window creation and other extraordinary events. [self setNeedsDisplayInRect:rect]; } -- (void)process_keyboard_event:(NSEvent*)event{ - b8 release = ([event type] == NSEventTypeKeyUp); - b8 down = !release; +- (void)process_keyboard_event:(NSEvent*)event down:(b8)down{ + b8 release = !down; Input_Modifier_Set_Fixed *mods = &mac_vars.input_chunk.pers.modifiers; // NOTE(yuval): Set control modifiers { Control_Keys *controls = &mac_vars.input_chunk.pers.controls; + b8 ctrl = (controls->r_ctrl || (controls->l_ctrl && !controls->r_alt)); b8 alt = (controls->l_alt || (controls->r_alt && !controls->l_ctrl)); if (mac_vars.lctrl_lalt_is_altgr && controls->l_alt && controls->l_ctrl){ @@ -674,8 +759,13 @@ this only gets called for window creation and other extraordinary events. alt = false; } + b8 shift = (controls->r_shift || controls->l_shift); + b8 command = (controls->r_command || controls->l_command); + set_modifier(mods, KeyCode_Control, ctrl); set_modifier(mods, KeyCode_Alt, alt); + set_modifier(mods, KeyCode_Shift, shift); + set_modifier(mods, KeyCode_Command, command); } // NOTE(yuval): Process KeyStroke / KeyRelease event @@ -711,41 +801,6 @@ this only gets called for window creation and other extraordinary events. system_signal_step(0); } } - - // NOTE(yuval): Process TextInput event - { - if (down){ - NSString *characters = [event characters]; - if ([characters length] > 0){ - // NOTE(yuval): Get the first utf-16 character - u32 c = [characters characterAtIndex:0]; - if (c == '\r'){ - c = '\n'; - } - - // NOTE(yuval): Check for a valid text input - if ((c > 127) || ((' ' <= c) && (c <= '~')) || (c == '\t') || (c == '\n') || (c == '\r')){ - String_Const_u16 str_16 = SCu16((u16*)&c, 1); - String_Const_u8 str_8 = string_u8_from_string_u16(mac_vars.frame_arena, str_16).string; - - Input_Event *event = push_input_event(mac_vars.frame_arena, &mac_vars.input_chunk.trans.event_list); - event->kind = InputEventKind_TextInsert; - event->text.string = str_8; - event->text.next_text = 0; - event->text.blocked = false; - if (mac_vars.active_text_input){ - mac_vars.active_text_input->text.next_text = event; - } else if (mac_vars.active_key_stroke){ - mac_vars.active_key_stroke->key.first_dependent_text = event; - } - - mac_vars.active_text_input = event; - - system_signal_step(0); - } - } - } - } } - (void)process_mouse_move_event:(NSEvent*)event{ From 020e2789d7e5f59e01862a9e9f636013148f29f7 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 12 Jan 2020 03:08:07 +0200 Subject: [PATCH 55/67] Fixed input bug where the input was not zeroed at the end of each frame. --- platform_mac/mac_4ed.mm | 170 +++++++++++++++++------------- platform_mac/mac_4ed_functions.mm | 18 ++-- 2 files changed, 106 insertions(+), 82 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index ec191dab..470c7365 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -129,6 +129,7 @@ struct Mac_Input_Chunk{ @end @interface FCoder_Window_Delegate : NSObject +- (void)process_focus_event; @end @interface FCoder_View : NSView @@ -458,6 +459,15 @@ mac_resize(NSWindow *window){ mac_resize(bounds.size.width, bounds.size.height); } +function inline void +mac_profile(char *name, u64 begin, u64 end){ + printf("%s Time: %fs\n", name, ((end - begin) / 1000000.0f)); +} + +#define MacProfileScope(name) for (u64 i = 0, begin = system_now_time();\ +i < 1;\ +++i, mac_profile(name, begin, system_now_time())) + //////////////////////////////// @implementation FCoder_App_Delegate @@ -491,16 +501,23 @@ mac_resize(NSWindow *window){ } - (void)windowDidBecomeKey:(NSNotification *)notification{ - printf("Focus\n"); - system_signal_step(0); + // NOTE(yuval): The window is the focused window + [self process_focus_event]; } - (void)windowDidResignKey:(NSNotification *)notification{ - printf("Lost Focus\n"); - system_signal_step(0); + // NOTE(yuval): The window has lost focus + [self process_focus_event]; } -- (void)windowDidEnterFullScreen:(NSNotification *)notification{ +- (void)process_focus_event{ + mac_vars.input_chunk.pers.mouse_l = false; + mac_vars.input_chunk.pers.mouse_r = false; + block_zero_struct(&mac_vars.input_chunk.pers.controls); + block_zero_struct(&mac_vars.input_chunk.pers.modifiers); + mac_vars.active_key_stroke = 0; + mac_vars.active_text_input = 0; + system_signal_step(0); } @end @@ -522,75 +539,82 @@ mac_resize(NSWindow *window){ } - (void)drawRect:(NSRect)bounds{ - // NOTE(yuval): Read comment in win32_4ed.cpp's main loop - system_mutex_acquire(mac_vars.global_frame_mutex); - - /* NOTE(yuval): Force the graphics context to clear to black so we don't -get a flash of white until the app is ready to draw. In practice on modern macOS, -this only gets called for window creation and other extraordinary events. -(Taken From SDL) */ - [[NSColor blackColor] setFill]; - NSRectFill(bounds); - - // NOTE(yuval): Prepare the Frame Input - Mac_Input_Chunk input_chunk = mac_vars.input_chunk; - Application_Step_Input input = {}; - - input.first_step = mac_vars.first; - input.dt = frame_useconds / 1000000.0f; - input.events = input_chunk.trans.event_list; - - input.mouse.out_of_window = input_chunk.trans.out_of_window; - - input.mouse.l = input_chunk.pers.mouse_l; - input.mouse.press_l = input_chunk.trans.mouse_l_press; - input.mouse.release_l = input_chunk.trans.mouse_l_release; - - input.mouse.r = input_chunk.pers.mouse_r; - input.mouse.press_r = input_chunk.trans.mouse_r_press; - input.mouse.release_r = input_chunk.trans.mouse_r_release; - - input.mouse.wheel = input_chunk.trans.mouse_wheel; - input.mouse.p = input_chunk.pers.mouse; - - input.trying_to_kill = input_chunk.trans.trying_to_kill; - - block_zero_struct(&mac_vars.input_chunk.trans); - - // NOTE(yuval): See comment in win32_4ed.cpp's main loop - if (mac_vars.send_exit_signal){ - input.trying_to_kill = true; - mac_vars.send_exit_signal = false; + MacProfileScope("Frame"){ + MacProfileScope("Acquire System Mutex"){ + // NOTE(yuval): Read comment in win32_4ed.cpp's main loop + system_mutex_acquire(mac_vars.global_frame_mutex); + } + + Mac_Input_Chunk input_chunk = mac_vars.input_chunk; + Application_Step_Input input = {}; + + // NOTE(yuval): Prepare the Frame Input + MacProfileScope("Prepare Input"){ + input.first_step = mac_vars.first; + input.dt = frame_useconds / 1000000.0f; + input.events = input_chunk.trans.event_list; + + input.mouse.out_of_window = input_chunk.trans.out_of_window; + + input.mouse.l = input_chunk.pers.mouse_l; + input.mouse.press_l = input_chunk.trans.mouse_l_press; + input.mouse.release_l = input_chunk.trans.mouse_l_release; + + input.mouse.r = input_chunk.pers.mouse_r; + input.mouse.press_r = input_chunk.trans.mouse_r_press; + input.mouse.release_r = input_chunk.trans.mouse_r_release; + + input.mouse.wheel = input_chunk.trans.mouse_wheel; + input.mouse.p = input_chunk.pers.mouse; + + input.trying_to_kill = input_chunk.trans.trying_to_kill; + + block_zero_struct(&mac_vars.input_chunk.trans); + mac_vars.active_key_stroke = 0; + mac_vars.active_text_input = 0; + + // NOTE(yuval): See comment in win32_4ed.cpp's main loop + if (mac_vars.send_exit_signal){ + input.trying_to_kill = true; + mac_vars.send_exit_signal = false; + } + } + + Application_Step_Result result = {}; + MacProfileScope("Step"){ + // NOTE(yuval): Application Core Update + if (app.step != 0){ + result = app.step(mac_vars.tctx, &target, mac_vars.base_ptr, &input); + } + } + + MacProfileScope("Perform Kill"){ + // NOTE(yuval): Quit the app + if (result.perform_kill){ + printf("Terminating 4coder!\n"); + [NSApp terminate:nil]; + } + } + + MacProfileScope("Render"){ + // NOTE(yuval): Render + renderer->render(renderer, &target); + + // NOTE(yuval): Schedule another step if needed + if (result.animating){ + system_signal_step(0); + } + } + + MacProfileScope("Cleanup"){ + mac_vars.first = false; + + linalloc_clear(mac_vars.frame_arena); + + system_mutex_release(mac_vars.global_frame_mutex); + } } - - // NOTE(yuval): Application Core Update - Application_Step_Result result = {}; - if (app.step != 0){ - result = app.step(mac_vars.tctx, &target, mac_vars.base_ptr, &input); - } - - // NOTE(yuval): Quit the app - if (result.perform_kill){ - printf("Terminating 4coder!\n"); - [NSApp terminate:nil]; - } - - // NOTE(yuval): Render - u64 begin_render = system_now_time(); - renderer->render(renderer, &target); - u64 end_render = system_now_time(); - printf("Render Time: %fs\n\n", (end_render - begin_render) / 1000000.0f); - - // NOTE(yuval): Schedule another step if needed - if (result.animating){ - system_signal_step(0); - } - - mac_vars.first = false; - - linalloc_clear(mac_vars.frame_arena); - - system_mutex_release(mac_vars.global_frame_mutex); + printf("\n"); } - (BOOL)acceptsFirstResponder{ diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index b62b4bb3..a39faae5 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -1,8 +1,8 @@ /* macOS System/Graphics/Font API Implementations */ -////////////////////// -// System API // -////////////////////// +/********************/ +/* System API */ +/********************/ //////////////////////////////// @@ -796,9 +796,9 @@ system_get_keyboard_modifiers_sig(){ //////////////////////////////// -//////////////////////// -// Graphics API // -//////////////////////// +/**********************/ +/* Graphics API */ +/**********************/ //////////////////////////////// @@ -816,9 +816,9 @@ graphics_fill_texture_sig(){ //////////////////////////////// -//////////////////// -// Font API // -//////////////////// +/******************/ +/* Font API */ +/******************/ //////////////////////////////// From dbcb05d2d1eee11d53eec3fe22776dc5fe1ead87 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 12 Jan 2020 23:16:03 +0200 Subject: [PATCH 56/67] Fixed a performance issue regarding timers. Now we can start a single timer per step request. --- metal/4ed_metal_render.mm | 2 +- platform_mac/mac_4ed.mm | 58 ++++++++++++++++++++++++++----- platform_mac/mac_4ed_functions.mm | 13 ++++--- platform_mac/mac_4ed_renderer.h | 4 ++- platform_mac/mac_4ed_renderer.mm | 25 +++++-------- 5 files changed, 72 insertions(+), 30 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 6f8b1a20..9aab846a 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -290,7 +290,7 @@ metal__make_buffer(u32 size, id device){ // NOTE(yuval): Obtain the render pass descriptor from the renderer's view MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor; if (render_pass_descriptor != nil){ - render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0f, 0.0f, 1.0f, 1.0f); + render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f); // NOTE(yuval): Create the render command encoder id render_encoder diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 470c7365..047086de 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -195,6 +195,7 @@ struct Mac_Vars { mach_timebase_info_data_t timebase_info; b32 first; + b32 step_requested; void *base_ptr; u64 timer_start; @@ -459,15 +460,18 @@ mac_resize(NSWindow *window){ mac_resize(bounds.size.width, bounds.size.height); } +#if defined(FRED_INTERNAL) function inline void mac_profile(char *name, u64 begin, u64 end){ printf("%s Time: %fs\n", name, ((end - begin) / 1000000.0f)); } #define MacProfileScope(name) for (u64 i = 0, begin = system_now_time();\ -i < 1;\ -++i, mac_profile(name, begin, system_now_time())) - +i == 0;\ +i = 1, mac_profile(name, begin, system_now_time())) +#else +# define MacProfileScope(...) +#endif //////////////////////////////// @implementation FCoder_App_Delegate @@ -539,8 +543,10 @@ i < 1;\ } - (void)drawRect:(NSRect)bounds{ - MacProfileScope("Frame"){ - MacProfileScope("Acquire System Mutex"){ + u64 prev_timer_start; + + MacProfileScope("Draw Rect"){ + MacProfileScope("Acquire Frame Mutex"){ // NOTE(yuval): Read comment in win32_4ed.cpp's main loop system_mutex_acquire(mac_vars.global_frame_mutex); } @@ -606,14 +612,44 @@ i < 1;\ } } + // NOTE(yuval): Sleep a bit to cool off + MacProfileScope("Cool Down"){ + system_mutex_release(mac_vars.global_frame_mutex); + { + u64 timer_end = system_now_time(); + u64 end_target = (mac_vars.timer_start + frame_useconds); + + if (timer_end < end_target){ + if ((end_target - timer_end) > 1000){ + // NOTE(yuval): Sleep until the end target minus a millisecond (to allow the scheduler to wake the process in time) + system_sleep(end_target - timer_end - 1000); + } + + // NOTE(yuval): Iterate through the rest of the time that's left using a regular for loop to make sure that we hit the end target + u64 now = system_now_time(); + while (now < end_target){ + now = system_now_time(); + } + } + + prev_timer_start = mac_vars.timer_start; + mac_vars.timer_start = system_now_time(); + } + system_mutex_acquire(mac_vars.global_frame_mutex); + } + MacProfileScope("Cleanup"){ mac_vars.first = false; + mac_vars.step_requested = false; linalloc_clear(mac_vars.frame_arena); + // NOTE(yuval): Release the global frame mutex until the next drawRect call system_mutex_release(mac_vars.global_frame_mutex); } } + + mac_profile("Frame", prev_timer_start, mac_vars.timer_start); printf("\n"); } @@ -762,6 +798,7 @@ i < 1;\ } - (void)request_display{ + printf("Display Requested!\n"); CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; @@ -829,7 +866,9 @@ i < 1;\ - (void)process_mouse_move_event:(NSEvent*)event{ NSPoint location = [event locationInWindow]; - Vec2_i32 new_m = V2i32(location.x, mac_vars.height - location.y); + NSPoint backing_location = [self convertPointToBacking:location]; + + Vec2_i32 new_m = V2i32(backing_location.x, mac_vars.height - backing_location.y); if (new_m != mac_vars.input_chunk.pers.mouse){ mac_vars.input_chunk.pers.mouse = new_m; } @@ -854,7 +893,7 @@ main(int arg_count, char **args){ pthread_mutex_init(&memory_tracker_mutex, 0); - // NOTE(yuval): Context Setup + // NOTE(yuval): Context setup Thread_Context _tctx = {}; thread_ctx_init(&_tctx, ThreadKind_Main, get_base_allocator_system(), @@ -1048,9 +1087,11 @@ main(int arg_count, char **args){ mac_resize(w, h); // - // TODO(yuval): Misc System Initializations + // NOTE(yuval): Misc System Initializations // + // TODO(yuval): Initialize clipboard buffer + mac_keycode_init(); // NOTE(yuval): Get the timebase info @@ -1072,6 +1113,7 @@ main(int arg_count, char **args){ // mac_vars.first = true; + mac_vars.step_requested = false; mac_vars.global_frame_mutex = system_mutex_make(); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index a39faae5..69275512 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -406,10 +406,15 @@ system_wake_up_timer_set_sig(){ function system_signal_step_sig(){ - [NSTimer scheduledTimerWithTimeInterval:0.0 - target:mac_vars.view - selector:@selector(request_display) - userInfo:nil repeats:NO]; + printf("Signal Step!\n"); + if (!mac_vars.step_requested){ + [NSTimer scheduledTimerWithTimeInterval:0.0 + target:mac_vars.view + selector:@selector(request_display) + userInfo:nil repeats:NO]; + + mac_vars.step_requested = true; + } } function diff --git a/platform_mac/mac_4ed_renderer.h b/platform_mac/mac_4ed_renderer.h index 0d1d4caf..ecc11ff2 100644 --- a/platform_mac/mac_4ed_renderer.h +++ b/platform_mac/mac_4ed_renderer.h @@ -21,7 +21,9 @@ typedef mac_fill_texture_sig(mac_fill_texture_type); typedef i32 Mac_Renderer_Kind; enum{ MacRenderer_OpenGL, - MacRenderer_Metal + MacRenderer_Metal, + // + MacRenderer_COUNT }; struct Mac_Renderer{ diff --git a/platform_mac/mac_4ed_renderer.mm b/platform_mac/mac_4ed_renderer.mm index 5b4e4015..7ad171b6 100644 --- a/platform_mac/mac_4ed_renderer.mm +++ b/platform_mac/mac_4ed_renderer.mm @@ -4,25 +4,18 @@ #import "mac_4ed_opengl.mm" #import "mac_4ed_metal.mm" +// TODO(yuval): Replace this array with an array of the paths to the renderer dlls +global mac_load_renderer_type *mac_renderer_load_functions[MacRenderer_COUNT] = { + mac_load_opengl_renderer, + mac_load_metal_renderer +}; + function Mac_Renderer* mac_init_renderer(Mac_Renderer_Kind kind, NSWindow *window, Render_Target *target){ - // TODO(yuval): Import renderer load function from a DLL instead of using a switch statement and a renderer kind. This would allow us to switch the renderer backend and implemented new backends with ease. + // TODO(yuval): Import renderer load function from a DLL instead of using an array of the load functions. This would allow us to switch the renderer backend and implemented new backends with ease. - Mac_Renderer *result = 0; - - switch (kind){ - case MacRenderer_OpenGL: - { - result = mac_load_opengl_renderer(window, target); - } break; - - case MacRenderer_Metal: - { - result = mac_load_metal_renderer(window, target); - } break; - - default: InvalidPath; - } + mac_load_renderer_type *load_renderer = mac_renderer_load_functions[kind]; + Mac_Renderer *result = load_renderer(window, target); if (!result){ mac_error_box("Unable to initialize the renderer!"); From 7d2a91805b7a953bef66bbbd533d339fc694cbe8 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 13 Jan 2020 03:05:44 +0200 Subject: [PATCH 57/67] The clipboard is now read is frame and on changes and sent to the core. We're using polling to get the clipboard contents on macOS using a timer because there is no way to get a clipboard change notification on macOS (sigh...) --- platform_mac/mac_4ed.mm | 103 +++++++++++++++++++++++++++++- platform_mac/mac_4ed_functions.mm | 1 - 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 047086de..564d3cd5 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -134,6 +134,7 @@ struct Mac_Input_Chunk{ @interface FCoder_View : NSView - (void)request_display; +- (void)check_clipboard; - (void)process_keyboard_event:(NSEvent*)event down:(b8)down; - (void)process_mouse_move_event:(NSEvent*)event; @end @@ -187,7 +188,10 @@ struct Mac_Vars { String_Const_u8 binary_path; + Arena *clipboard_arena; String_Const_u8 clipboard_contents; + u32 clipboard_change_count; + b32 next_clipboard_is_self; NSWindow *window; FCoder_View *view; @@ -460,6 +464,46 @@ mac_resize(NSWindow *window){ mac_resize(bounds.size.width, bounds.size.height); } +function u32 +mac_get_clipboard_change_count(void){ + NSPasteboard *board = [NSPasteboard generalPasteboard]; + u32 result = board.changeCount; + + return(result); +} + +function b32 +mac_read_clipboard_contents(Arena *scratch){ + b32 result = false; + + Temp_Memory temp = begin_temp(scratch); + { + NSPasteboard *board = [NSPasteboard generalPasteboard]; + NSString *utf8_type = @"public.utf8-plain-text"; + NSArray *array = [NSArray arrayWithObjects:utf8_type, nil]; + NSString *has_string = [board availableTypeFromArray:array]; + if (has_string != nil){ + NSData *data = [board dataForType:utf8_type]; + if (data != nil){ + u32 copy_length = data.length; + if (copy_length > 0){ + Arena *clip_arena = mac_vars.clipboard_arena; + linalloc_clear(clip_arena); + + mac_vars.clipboard_contents = string_const_u8_push(clip_arena, copy_length); + [data getBytes:mac_vars.clipboard_contents.str + length:mac_vars.clipboard_contents.size]; + + result = true; + } + } + } + } + end_temp(temp); + + return(result); +} + #if defined(FRED_INTERNAL) function inline void mac_profile(char *name, u64 begin, u64 end){ @@ -551,11 +595,12 @@ i = 1, mac_profile(name, begin, system_now_time())) system_mutex_acquire(mac_vars.global_frame_mutex); } - Mac_Input_Chunk input_chunk = mac_vars.input_chunk; Application_Step_Input input = {}; // NOTE(yuval): Prepare the Frame Input MacProfileScope("Prepare Input"){ + Mac_Input_Chunk input_chunk = mac_vars.input_chunk; + input.first_step = mac_vars.first; input.dt = frame_useconds / 1000000.0f; input.events = input_chunk.trans.event_list; @@ -586,6 +631,34 @@ i = 1, mac_profile(name, begin, system_now_time())) } } + // NOTE(yuval): Frame clipboard input + MacProfileScope("Frame Clipboard Input"){ + block_zero_struct(&mac_vars.clipboard_contents); + input.clipboard_changed = false; + + if (mac_vars.clipboard_change_count != 0){ + u32 change_count = mac_get_clipboard_change_count(); + if (change_count != mac_vars.clipboard_change_count){ + if (mac_vars.next_clipboard_is_self){ + mac_vars.next_clipboard_is_self = false; + } else{ + for (i32 r = 0; r < 4; ++r){ + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + + if (mac_read_clipboard_contents(scratch)){ + input.clipboard_changed = true; + break; + } + } + } + + mac_vars.clipboard_change_count = change_count; + } + } + + input.clipboard = mac_vars.clipboard_contents; + } + Application_Step_Result result = {}; MacProfileScope("Step"){ // NOTE(yuval): Application Core Update @@ -798,12 +871,18 @@ i = 1, mac_profile(name, begin, system_now_time())) } - (void)request_display{ - printf("Display Requested!\n"); CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; } +- (void)check_clipboard{ + u32 change_count = mac_get_clipboard_change_count(); + if (change_count != mac_vars.clipboard_change_count){ + system_signal_step(0); + } +} + - (void)process_keyboard_event:(NSEvent*)event down:(b8)down{ b8 release = !down; @@ -1090,8 +1169,26 @@ main(int arg_count, char **args){ // NOTE(yuval): Misc System Initializations // - // TODO(yuval): Initialize clipboard buffer + // NOTE(yuval): Initialize clipboard + { + mac_vars.clipboard_arena = reserve_arena(mac_vars.tctx); + mac_vars.clipboard_change_count = mac_get_clipboard_change_count(); + mac_vars.next_clipboard_is_self = false; + + // NOTE(yuval): Read the current clipboard + { + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + mac_read_clipboard_contents(scratch); + } + + // NOTE(yuval): Start the clipboard polling timer + [NSTimer scheduledTimerWithTimeInterval: 0.5 + target:mac_vars.view + selector:@selector(check_clipboard) + userInfo:nil repeats:YES]; + } + // NOTE(yuval): Initialize the virtul keycodes table mac_keycode_init(); // NOTE(yuval): Get the timebase info diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 69275512..aeeaab27 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -406,7 +406,6 @@ system_wake_up_timer_set_sig(){ function system_signal_step_sig(){ - printf("Signal Step!\n"); if (!mac_vars.step_requested){ [NSTimer scheduledTimerWithTimeInterval:0.0 target:mac_vars.view From f7b0b05426b539cebd041607f6ab4c72dbcd0013 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 13 Jan 2020 03:20:58 +0200 Subject: [PATCH 58/67] Posting to clipboard now works. --- platform_mac/mac_4ed.mm | 36 +++++++++++++++++++++++++++++-- platform_mac/mac_4ed_functions.mm | 16 +++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 564d3cd5..ba62017d 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -193,6 +193,9 @@ struct Mac_Vars { u32 clipboard_change_count; b32 next_clipboard_is_self; + Arena clip_post_arena; + String_Const_u8 clip_post; + NSWindow *window; FCoder_View *view; f32 screen_scale_factor; @@ -480,8 +483,8 @@ mac_read_clipboard_contents(Arena *scratch){ { NSPasteboard *board = [NSPasteboard generalPasteboard]; NSString *utf8_type = @"public.utf8-plain-text"; - NSArray *array = [NSArray arrayWithObjects:utf8_type, nil]; - NSString *has_string = [board availableTypeFromArray:array]; + NSArray *types_array = [NSArray arrayWithObjects:utf8_type, nil]; + NSString *has_string = [board availableTypeFromArray:types_array]; if (has_string != nil){ NSData *data = [board dataForType:utf8_type]; if (data != nil){ @@ -504,6 +507,25 @@ mac_read_clipboard_contents(Arena *scratch){ return(result); } +function void +mac_post_clipboard(Arena *scratch, char *text, i32 len){ + NSPasteboard *board = [NSPasteboard generalPasteboard]; + + NSString *utf8_type = @"public.utf8-plain-text"; + NSArray *types_array = [NSArray arrayWithObjects:utf8_type, nil]; + [board declareTypes:types_array + owner:nil]; + + NSString *paste_string = [[NSString alloc] initWithBytes:text + length:len + encoding:NSUTF8StringEncoding]; + [board setString:paste_string + forType:utf8_type]; + [paste_string release]; + + mac_vars.next_clipboard_is_self = true; +} + #if defined(FRED_INTERNAL) function inline void mac_profile(char *name, u64 begin, u64 end){ @@ -659,6 +681,8 @@ i = 1, mac_profile(name, begin, system_now_time())) input.clipboard = mac_vars.clipboard_contents; } + mac_vars.clip_post.size = 0; + Application_Step_Result result = {}; MacProfileScope("Step"){ // NOTE(yuval): Application Core Update @@ -675,6 +699,14 @@ i = 1, mac_profile(name, begin, system_now_time())) } } + // NOTE(yuval): Post new clipboard content + MacProfileScope("Post Clipboard"){ + if (mac_vars.clip_post.size > 0){ + Scratch_Block scratch(mac_vars.tctx, Scratch_Share); + mac_post_clipboard(scratch, (char*)mac_vars.clip_post.str, (i32)mac_vars.clip_post.size); + } + } + MacProfileScope("Render"){ // NOTE(yuval): Render renderer->render(renderer, &target); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index aeeaab27..9076a100 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -431,7 +431,21 @@ system_sleep_sig(){ function system_post_clipboard_sig(){ - NotImplemented; + Arena *arena = &mac_vars.clip_post_arena; + if (arena->base_allocator == 0){ + *arena = make_arena_system(); + } else{ + linalloc_clear(arena); + } + + mac_vars.clip_post.str = push_array(arena, u8, str.size + 1); + if (mac_vars.clip_post.str != 0){ + block_copy(mac_vars.clip_post.str, str.str, str.size); + mac_vars.clip_post.str[str.size] = 0; + mac_vars.clip_post.size = str.size; + } else{ + // NOTE(yuval): Failed to allocate buffer for clipboard post + } } //////////////////////////////// From fd1c41bab48483ee48c58c60796c2eeccc572c0c Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 13 Jan 2020 04:06:10 +0200 Subject: [PATCH 59/67] Finished implementing the run loop. --- platform_mac/mac_4ed.mm | 120 +++++++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 13 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index ba62017d..e8b58a63 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -185,6 +185,10 @@ struct Mac_Vars { i32 cursor_show; i32 prev_cursor_show; + NSCursor *cursor_ibeam; + NSCursor *cursor_arrow; + NSCursor *cursor_leftright; + NSCursor *cursor_updown; String_Const_u8 binary_path; @@ -437,6 +441,8 @@ mac_file_can_be_made(u8* filename){ return(result); } +//////////////////////////////// + function void mac_resize(float width, float height){ if ((width > 0.0f) && (height > 0.0f)){ @@ -467,6 +473,8 @@ mac_resize(NSWindow *window){ mac_resize(bounds.size.width, bounds.size.height); } +//////////////////////////////// + function u32 mac_get_clipboard_change_count(void){ NSPasteboard *board = [NSPasteboard generalPasteboard]; @@ -526,18 +534,28 @@ mac_post_clipboard(Arena *scratch, char *text, i32 len){ mac_vars.next_clipboard_is_self = true; } +//////////////////////////////// + +function void +mac_toggle_fullscreen(void){ + [mac_vars.window toggleFullScreen:nil]; +} + +//////////////////////////////// + #if defined(FRED_INTERNAL) function inline void mac_profile(char *name, u64 begin, u64 end){ printf("%s Time: %fs\n", name, ((end - begin) / 1000000.0f)); } -#define MacProfileScope(name) for (u64 i = 0, begin = system_now_time();\ -i == 0;\ -i = 1, mac_profile(name, begin, system_now_time())) +#define MacProfileScope(name) for (u64 glue(_i_, __LINE__) = 0, glue(_begin_, __LINE__) = system_now_time();\ +glue(_i_, __LINE__) == 0;\ +glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_time())) #else # define MacProfileScope(...) #endif + //////////////////////////////// @implementation FCoder_App_Delegate @@ -683,16 +701,16 @@ i = 1, mac_profile(name, begin, system_now_time())) mac_vars.clip_post.size = 0; + // NOTE(yuval): Application Core Update Application_Step_Result result = {}; MacProfileScope("Step"){ - // NOTE(yuval): Application Core Update if (app.step != 0){ result = app.step(mac_vars.tctx, &target, mac_vars.base_ptr, &input); } } + // NOTE(yuval): Quit the app if requested by the application core MacProfileScope("Perform Kill"){ - // NOTE(yuval): Quit the app if (result.perform_kill){ printf("Terminating 4coder!\n"); [NSApp terminate:nil]; @@ -707,11 +725,75 @@ i = 1, mac_profile(name, begin, system_now_time())) } } - MacProfileScope("Render"){ - // NOTE(yuval): Render - renderer->render(renderer, &target); + // NOTE(yuval): Switch to a new title + MacProfileScope("Switch Title"){ + if (result.has_new_title){ + NSString *str = [NSString stringWithUTF8String:result.title_string]; + [mac_vars.window setTitle:str]; + } + } + + // NOTE(yuval): Switch to new cursor + MacProfileScope("Switch Cursor"){ + // NOTE(yuval): Switch cursor type + switch (result.mouse_cursor_type){ + case APP_MOUSE_CURSOR_ARROW: + { + [mac_vars.cursor_arrow set]; + } break; + + case APP_MOUSE_CURSOR_IBEAM: + { + [mac_vars.cursor_ibeam set]; + } break; + + case APP_MOUSE_CURSOR_LEFTRIGHT: + { + [mac_vars.cursor_leftright set]; + } break; + + case APP_MOUSE_CURSOR_UPDOWN: + { + [mac_vars.cursor_updown set]; + } break; + } - // NOTE(yuval): Schedule another step if needed + // NOTE(yuval): Show or hide cursor + if (mac_vars.cursor_show != mac_vars.prev_cursor_show){ + switch (mac_vars.cursor_show){ + case MouseCursorShow_Never: + { + [NSCursor hide]; + } break; + + case MouseCursorShow_Always: + { + [NSCursor unhide]; + } break; + } + + mac_vars.prev_cursor_show = mac_vars.cursor_show; + } + } + + // NOTE(yuval): Update lctrl_lalt_is_altgr status + mac_vars.lctrl_lalt_is_altgr = (b8)result.lctrl_lalt_is_altgr; + + // NOTE(yuval): Render + MacProfileScope("Render"){ + renderer->render(renderer, &target); + } + + // NOTE(yuval): Toggle full screen + MacProfileScope("Toggle Full Screen"){ + if (mac_vars.do_toggle){ + mac_toggle_fullscreen(); + mac_vars.do_toggle = false; + } + } + + // NOTE(yuval): Schedule another step if needed + MacProfileScope("Schedule Animations"){ if (result.animating){ system_signal_step(0); } @@ -1026,9 +1108,6 @@ main(int arg_count, char **args){ mac_vars.frame_arena = reserve_arena(mac_vars.tctx); target.arena = make_arena_system(KB(256)); - mac_vars.cursor_show = MouseCursorShow_Always; - mac_vars.prev_cursor_show = MouseCursorShow_Always; - dll_init_sentinel(&mac_vars.free_mac_objects); dll_init_sentinel(&mac_vars.timer_objects); @@ -1178,7 +1257,7 @@ main(int arg_count, char **args){ [mac_vars.window setMinSize:NSMakeSize(100, 100)]; [mac_vars.window setBackgroundColor:NSColor.blackColor]; - [mac_vars.window setTitle:@WINDOW_NAME]; + [mac_vars.window setTitle:@"GRAPHICS"]; [mac_vars.window setAcceptsMouseMovedEvents:YES]; NSView* content_view = [mac_vars.window contentView]; @@ -1223,6 +1302,17 @@ main(int arg_count, char **args){ // NOTE(yuval): Initialize the virtul keycodes table mac_keycode_init(); + // NOTE(yuval): Initialize cursors + { + mac_vars.cursor_show = MouseCursorShow_Always; + mac_vars.prev_cursor_show = MouseCursorShow_Always; + + mac_vars.cursor_arrow = [NSCursor arrowCursor]; + mac_vars.cursor_ibeam = [NSCursor IBeamCursor]; + mac_vars.cursor_leftright = [NSCursor resizeLeftRightCursor]; + mac_vars.cursor_updown = [NSCursor resizeUpDownCursor]; + } + // NOTE(yuval): Get the timebase info mach_timebase_info(&mac_vars.timebase_info); @@ -1244,6 +1334,10 @@ main(int arg_count, char **args){ mac_vars.first = true; mac_vars.step_requested = false; + if (plat_settings.fullscreen_window){ + mac_toggle_fullscreen(); + } + mac_vars.global_frame_mutex = system_mutex_make(); mac_vars.timer_start = system_now_time(); From 585978982e37623249e65d2334fd0715af0e8a93 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 13 Jan 2020 04:20:10 +0200 Subject: [PATCH 60/67] Updates to the step scheduling to improve animations. --- platform_mac/mac_4ed.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index e8b58a63..5f444f51 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -630,6 +630,8 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t u64 prev_timer_start; MacProfileScope("Draw Rect"){ + mac_vars.step_requested = false; + MacProfileScope("Acquire Frame Mutex"){ // NOTE(yuval): Read comment in win32_4ed.cpp's main loop system_mutex_acquire(mac_vars.global_frame_mutex); @@ -827,7 +829,6 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t MacProfileScope("Cleanup"){ mac_vars.first = false; - mac_vars.step_requested = false; linalloc_clear(mac_vars.frame_arena); @@ -985,6 +986,7 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } - (void)request_display{ + printf("Display Requested!\n"); CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; From a737a5409ac117f3df77f559aa64a6463c89731e Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Mon, 13 Jan 2020 16:15:25 +0200 Subject: [PATCH 61/67] Implemented all system cli handling functions. --- platform_mac/mac_4ed.mm | 9 ++- platform_mac/mac_4ed_functions.mm | 92 ++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 5f444f51..00735e43 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -206,9 +206,11 @@ struct Mac_Vars { mach_timebase_info_data_t timebase_info; b32 first; - b32 step_requested; void *base_ptr; + u64 timer_start; + b32 step_requested; + i32 running_cli; Node free_mac_objects; Node timer_objects; @@ -795,8 +797,8 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } // NOTE(yuval): Schedule another step if needed - MacProfileScope("Schedule Animations"){ - if (result.animating){ + MacProfileScope("Schedule Step"){ + if (result.animating || (mac_vars.running_cli > 0)){ system_signal_step(0); } } @@ -1335,6 +1337,7 @@ main(int arg_count, char **args){ mac_vars.first = true; mac_vars.step_requested = false; + mac_vars.running_cli = 0; if (plat_settings.fullscreen_window){ mac_toggle_fullscreen(); diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 9076a100..1502a441 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -454,32 +454,108 @@ function system_cli_call_sig(){ b32 result = false; - NotImplemented; + int pipe_fds[2]; + if (pipe(pipe_fds) == -1){ + perror("system_cli_call: pipe"); + return(false); + } - return(result); + pid_t child_pid = fork(); + if (child_pid == -1){ + perror("system_cli_call: fork"); + return(false); + } + + enum { PIPE_FD_READ, PIPE_FD_WRITE }; + + if (child_pid == 0){ + // NOTE(yuval): Child Process + 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); + } + + char* argv[] = {"sh", "-c", script, 0}; + + if (execv("/bin/sh", argv) == -1){ + perror("system_cli_call: execv"); + } + + exit(1); + } else{ + // NOTE(yuval): Parent Process + 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]; + + mac_vars.running_cli += 1; + } + + return(true); } function system_cli_begin_update_sig(){ - NotImplemented; + // NOTE(yuval): Nothing to do here. } function system_cli_update_step_sig(){ - b32 result = false; + int pipe_read_fd = *(int*)&cli->out_read; - NotImplemented; + 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); + + b32 result = ((ptr - dest) > 0); return(result); } function system_cli_end_update_sig(){ - b32 result = false; + b32 close_me = false; - NotImplemented; + pid_t pid = *(pid_t*)&cli->proc; - return(result); + int status; + if (pid && (waitpid(pid, &status, WNOHANG) > 0)){ + cli->exit = WEXITSTATUS(status); + + close(*(int*)&cli->out_read); + close(*(int*)&cli->out_write); + + mac_vars.running_cli -= 1; + + close_me = true; + } + + return(close_me); } //////////////////////////////// From de9fc34c3e25f70dacb930bcfcb753390a0813ed Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 14 Jan 2020 03:22:29 +0200 Subject: [PATCH 62/67] Added a hack to fix DeadLock problem regrading the system_memory_annotation function. Also started working on fixing the performance issues that I discovered in the graphics renderer on retina displays (for now I changed to FCoder_View to be layer backed which is faster for rendering animations, this seems to improve performance by a little margin but rendering on high dpi displays is not closed to being smooth yet...). --- 4ed.cpp | 1684 ++++++++++++++--------------- custom/4coder_fancy.cpp | 48 +- metal/4ed_metal_render.mm | 23 +- platform_mac/mac_4ed.mm | 50 +- platform_mac/mac_4ed_functions.mm | 4 +- platform_mac/mac_4ed_metal.mm | 4 - 6 files changed, 916 insertions(+), 897 deletions(-) diff --git a/4ed.cpp b/4ed.cpp index 8059d581..60b9f849 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -1,842 +1,842 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 12.12.2014 - * - * Application layer for project codename "4ed" - * - */ - -// TOP - -internal void -init_command_line_settings(App_Settings *settings, Plat_Settings *plat_settings, i32 argc, char **argv){ - char *arg = 0; - Command_Line_Mode mode = CLMode_App; - Command_Line_Action action = CLAct_Nothing; - b32 strict = false; - - settings->init_files_max = ArrayCount(settings->init_files); - for (i32 i = 1; i <= argc; ++i){ - if (i == argc){ - arg = ""; - } - else{ - arg = argv[i]; - } - - if (arg[0] == '-' && arg[1] == '-'){ - char *long_arg_name = arg+2; - if (string_match(SCu8(long_arg_name), string_u8_litexpr("custom"))){ - mode = CLMode_Custom; - continue; - } - } - - switch (mode){ - case CLMode_App: - { - switch (action){ - case CLAct_Nothing: - { - if (arg[0] == '-'){ - action = CLAct_Ignore; - switch (arg[1]){ - case 'd': action = CLAct_CustomDLL; strict = false; break; - case 'D': action = CLAct_CustomDLL; strict = true; break; - - case 'i': action = CLAct_InitialFilePosition; break; - - case 'w': action = CLAct_WindowSize; break; - case 'W': action = CLAct_WindowMaximize; break; - case 'p': action = CLAct_WindowPosition; break; - case 'F': action = CLAct_WindowFullscreen; break; - - case 'f': action = CLAct_FontSize; break; - case 'h': action = CLAct_FontUseHinting; --i; break; - } - } - else if (arg[0] != 0){ - if (settings->init_files_count < settings->init_files_max){ - i32 index = settings->init_files_count++; - settings->init_files[index] = arg; - } - } - }break; - - case CLAct_CustomDLL: - { - plat_settings->custom_dll_is_strict = (b8)strict; - if (i < argc){ - plat_settings->custom_dll = argv[i]; - } - action = CLAct_Nothing; - }break; - - case CLAct_InitialFilePosition: - { - if (i < argc){ - settings->initial_line = (i32)string_to_integer(SCu8(argv[i]), 10); - } - action = CLAct_Nothing; - }break; - - case CLAct_WindowSize: - { - if (i + 1 < argc){ - plat_settings->set_window_size = true; - - i32 w = (i32)string_to_integer(SCu8(argv[i]), 10); - i32 h = (i32)string_to_integer(SCu8(argv[i + 1]), 10); - if (w > 0){ - plat_settings->window_w = w; - } - if (h > 0){ - plat_settings->window_h = h; - } - - ++i; - } - action = CLAct_Nothing; - }break; - - case CLAct_WindowMaximize: - { - --i; - plat_settings->maximize_window = true; - action = CLAct_Nothing; - }break; - - case CLAct_WindowPosition: - { - if (i + 1 < argc){ - plat_settings->set_window_pos = true; - - i32 x = (i32)string_to_integer(SCu8(argv[i]), 10); - i32 y = (i32)string_to_integer(SCu8(argv[i + 1]), 10); - if (x > 0){ - plat_settings->window_x = x; - } - if (y > 0){ - plat_settings->window_y = y; - } - - ++i; - } - action = CLAct_Nothing; - }break; - - case CLAct_WindowFullscreen: - { - --i; - plat_settings->fullscreen_window = true; - action = CLAct_Nothing; - }break; - - case CLAct_FontSize: - { - if (i < argc){ - settings->font_size = (i32)string_to_integer(SCu8(argv[i]), 10); - } - action = CLAct_Nothing; - }break; - - case CLAct_FontUseHinting: - { - plat_settings->use_hinting = true; - settings->use_hinting = plat_settings->use_hinting; - action = CLAct_Nothing; - }break; - } - }break; - - case CLMode_Custom: - { - settings->custom_flags = argv + i; - settings->custom_flags_count = argc - i; - i = argc; - mode = CLMode_App; - }break; - } - } -} - -//////////////////////////////// - -internal Models* -models_init(void){ - Arena arena = make_arena_system(); - Models *models = push_array_zero(&arena, Models, 1); - models->arena_ = arena; - models->arena = &models->arena_; - heap_init(&models->heap, get_base_allocator_system()); - return(models); -} - -internal void -app_load_vtables(API_VTable_system *vtable_system, API_VTable_font *vtable_font, API_VTable_graphics *vtable_graphics){ - system_api_read_vtable(vtable_system); - font_api_read_vtable(vtable_font); - graphics_api_read_vtable(vtable_graphics); -} - -internal Log_Function* -app_get_logger(void){ - log_init(); - return(log_string); -} - -App_Read_Command_Line_Sig(app_read_command_line){ - Models *models = models_init(); - App_Settings *settings = &models->settings; - block_zero_struct(settings); - if (argc > 1){ - init_command_line_settings(&models->settings, plat_settings, argc, argv); - } - *files = models->settings.init_files; - *file_count = &models->settings.init_files_count; - return(models); -} - -App_Init_Sig(app_init){ - Models *models = (Models*)base_ptr; - models->keep_playing = true; - - models->config_api = api; - models->virtual_event_arena = reserve_arena(tctx); - - profile_init(&models->profile_list); - - managed_ids_init(tctx->allocator, &models->managed_id_set); - - API_VTable_custom custom_vtable = {}; - custom_api_fill_vtable(&custom_vtable); - API_VTable_system system_vtable = {}; - system_api_fill_vtable(&system_vtable); - Custom_Layer_Init_Type *custom_init = api.init_apis(&custom_vtable, &system_vtable); - Assert(custom_init != 0); - - // NOTE(allen): coroutines - coroutine_system_init(&models->coroutines); - - // NOTE(allen): font set - font_set_init(&models->font_set); - - // NOTE(allen): live set - Arena *arena = models->arena; - { - models->view_set.count = 0; - models->view_set.max = MAX_VIEWS; - models->view_set.views = push_array(arena, View, models->view_set.max); - - //dll_init_sentinel - models->view_set.free_sentinel.next = &models->view_set.free_sentinel; - models->view_set.free_sentinel.prev = &models->view_set.free_sentinel; - - i32 max = models->view_set.max; - View *view = models->view_set.views; - for (i32 i = 0; i < max; ++i, ++view){ - //dll_insert(&models->view_set.free_sentinel, view); - view->next = models->view_set.free_sentinel.next; - view->prev = &models->view_set.free_sentinel; - models->view_set.free_sentinel.next = view; - view->next->prev = view; - } - } - - lifetime_allocator_init(tctx->allocator, &models->lifetime_allocator); - dynamic_workspace_init(&models->lifetime_allocator, DynamicWorkspace_Global, 0, &models->dynamic_workspace); - - // NOTE(allen): file setup - working_set_init(models, &models->working_set); - Mutex_Lock file_order_lock(models->working_set.mutex); - - // NOTE(allen): - global_history_init(&models->global_history); - text_layout_init(tctx, &models->text_layouts); - - // NOTE(allen): 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): do(better clipboard allocation) - if (clipboard.str != 0){ - String_Const_u8 *dest = working_set_next_clipboard_string(&models->heap, &models->working_set, clipboard.size); - block_copy(dest->str, clipboard.str, clipboard.size); - } - - // NOTE(allen): style setup - { - Scratch_Block scratch(tctx); - Face_Description description = {}; - description.font.file_name = get_file_path_in_fonts_folder(scratch, string_u8_litexpr("liberation-mono.ttf")); - description.parameters.pt_size = 12; - Face *new_face = font_set_new_face(&models->font_set, &description); - models->global_face_id = new_face->id; - } - - // NOTE(allen): title space - models->has_new_title = true; - models->title_capacity = KB(4); - models->title_space = push_array(arena, char, models->title_capacity); - block_copy(models->title_space, WINDOW_NAME, sizeof(WINDOW_NAME)); - - // NOTE(allen): miscellaneous init - hot_directory_init(arena, &models->hot_directory, current_directory); - child_process_container_init(tctx->allocator, &models->child_processes); - models->period_wakeup_timer = system_wake_up_timer_create(); - - // NOTE(allen): custom layer init - Application_Links app = {}; - app.tctx = tctx; - app.cmd_context = models; - custom_init(&app); - - // NOTE(allen): init baked in buffers - File_Init init_files[] = { - { string_u8_litinit("*messages*"), &models->message_buffer , true , }, - { string_u8_litinit("*scratch*") , &models->scratch_buffer , false, }, - { string_u8_litinit("*log*") , &models->log_buffer , true , }, - { string_u8_litinit("*keyboard*"), &models->keyboard_buffer, true , }, - }; - - Heap *heap = &models->heap; - for (i32 i = 0; i < ArrayCount(init_files); ++i){ - Editing_File *file = working_set_allocate_file(&models->working_set, &models->lifetime_allocator); - buffer_bind_name(tctx, models, arena, &models->working_set, file, init_files[i].name); - - if (init_files[i].ptr != 0){ - *init_files[i].ptr = file; - } - - File_Attributes attributes = {}; - file_create_from_string(tctx, models, file, SCu8(), attributes); - if (init_files[i].read_only){ - file->settings.read_only = true; - history_free(tctx, &file->state.history); - } - - file->settings.never_kill = true; - file_set_unimportant(file, true); - } - - // NOTE(allen): setup first panel - { - Panel *panel = layout_initialize(arena, &models->layout); - View *new_view = live_set_alloc_view(&models->lifetime_allocator, &models->view_set, panel); - view_init(tctx, models, new_view, models->scratch_buffer, models->view_event_handler); - } -} - -App_Step_Sig(app_step){ - Models *models = (Models*)base_ptr; - - Mutex_Lock file_order_lock(models->working_set.mutex); - Scratch_Block scratch(tctx, Scratch_Share); - - models->next_animate_delay = max_u32; - models->animate_next_frame = false; - - // NOTE(allen): per-frame update of models state - begin_frame(target, &models->font_set); - models->target = target; - models->input = input; - - // NOTE(allen): OS clipboard event handling - String_Const_u8 clipboard = input->clipboard; - if (clipboard.str != 0){ - String_Const_u8 *dest = working_set_next_clipboard_string(&models->heap, &models->working_set, clipboard.size); - dest->size = eol_convert_in((char*)dest->str, (char*)clipboard.str, (i32)clipboard.size); - if (input->clipboard_changed){ - co_send_core_event(tctx, models, CoreCode_NewClipboardContents, *dest); - } - } - - // NOTE(allen): reorganizing panels on screen - Vec2_i32 prev_dim = layout_get_root_size(&models->layout); - Vec2_i32 current_dim = V2i32(target->width, target->height); - layout_set_root_size(&models->layout, current_dim); - - // NOTE(allen): update child processes - f32 dt = input->dt; - if (dt > 0){ - Temp_Memory_Block temp(scratch); - - Child_Process_Container *child_processes = &models->child_processes; - Child_Process **processes_to_free = push_array(scratch, Child_Process*, child_processes->active_child_process_count); - i32 processes_to_free_count = 0; - - u32 max = KB(128); - char *dest = push_array(scratch, char, max); - - for (Node *node = child_processes->child_process_active_list.next; - node != &child_processes->child_process_active_list; - node = node->next){ - Child_Process *child_process = CastFromMember(Child_Process, node, node); - - Editing_File *file = child_process->out_file; - CLI_Handles *cli = &child_process->cli; - - // TODO(allen): do(call a 'child process updated hook' let that hook populate the buffer if it so chooses) - - b32 edited_file = false; - u32 amount = 0; - system_cli_begin_update(cli); - if (system_cli_update_step(cli, dest, max, &amount)){ - if (file != 0 && amount > 0){ - amount = eol_in_place_convert_in(dest, amount); - output_file_append(tctx, models, file, SCu8(dest, amount)); - edited_file = true; - } - } - - if (system_cli_end_update(cli)){ - if (file != 0){ - String_Const_u8 str = push_u8_stringf(scratch, "exited with code %d", cli->exit); - output_file_append(tctx, models, file, str); - edited_file = true; - } - processes_to_free[processes_to_free_count++] = child_process; - child_process_set_return_code(models, child_processes, child_process->id, cli->exit); - } - - if (child_process->cursor_at_end && file != 0){ - file_cursor_to_end(tctx, models, file); - } - } - - for (i32 i = 0; i < processes_to_free_count; ++i){ - child_process_free(child_processes, processes_to_free[i]->id); - } - } - - // NOTE(allen): simulated events - Input_List input_list = input->events; - Input_Modifier_Set modifiers = system_get_keyboard_modifiers(scratch); - if (input->mouse.press_l){ - Input_Event event = {}; - event.kind = InputEventKind_MouseButton; - event.mouse.code = MouseCode_Left; - event.mouse.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - else if (input->mouse.release_l){ - Input_Event event = {}; - event.kind = InputEventKind_MouseButtonRelease; - event.mouse.code = MouseCode_Left; - event.mouse.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - if (input->mouse.press_r){ - Input_Event event = {}; - event.kind = InputEventKind_MouseButton; - event.mouse.code = MouseCode_Right; - event.mouse.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - else if (input->mouse.release_r){ - Input_Event event = {}; - event.kind = InputEventKind_MouseButtonRelease; - event.mouse.code = MouseCode_Right; - event.mouse.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - if (input->mouse.wheel != 0){ - Input_Event event = {}; - event.kind = InputEventKind_MouseWheel; - event.mouse_wheel.value = (f32)(input->mouse.wheel); - event.mouse_wheel.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - if (input->mouse.p != models->prev_p){ - b32 was_in_window = rect_contains_point(Ri32(0, 0, prev_dim.x, prev_dim.y), models->prev_p); - b32 is_in_window = rect_contains_point(Ri32(0, 0, current_dim.x, current_dim.y), input->mouse.p); - if (is_in_window || was_in_window){ - Input_Event event = {}; - event.kind = InputEventKind_MouseMove; - event.mouse_move.p = input->mouse.p; - event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); - push_input_event(scratch, &input_list, &event); - } - } - if (models->animated_last_frame){ - Input_Event event = {}; - event.kind = InputEventKind_Core; - event.core.code = CoreCode_Animate; - push_input_event(scratch, &input_list, &event); - } - - // NOTE(allen): expose layout - Layout *layout = &models->layout; - - // NOTE(allen): mouse hover status - Panel *mouse_panel = 0; - Panel *divider_panel = 0; - b32 mouse_in_margin = false; - Vec2_i32 mouse = input->mouse.p; - { - for (Panel *panel = layout_get_first_open_panel(layout); - panel != 0; - panel = layout_get_next_open_panel(layout, panel)){ - if (rect_contains_point(panel->rect_full, mouse)){ - mouse_panel = panel; - if (!rect_contains_point(panel->rect_inner, mouse)){ - mouse_in_margin = true; - for (divider_panel = mouse_panel->parent; - divider_panel != 0; - divider_panel = divider_panel->parent){ - if (rect_contains_point(divider_panel->rect_inner, mouse)){ - break; - } - } - } - } - if (mouse_panel != 0){ - break; - } - } - } - - // NOTE(allen): First frame initialization - if (input->first_step){ - Temp_Memory_Block temp(scratch); - - String_Const_u8_Array file_names = {}; - file_names.count = models->settings.init_files_count; - file_names.vals = push_array(scratch, String_Const_u8, file_names.count); - for (i32 i = 0; i < file_names.count; i += 1){ - file_names.vals[i] = SCu8(models->settings.init_files[i]); - } - - String_Const_u8_Array flags = {}; - flags.count = models->settings.custom_flags_count; - flags.vals = push_array(scratch, String_Const_u8, flags.count); - for (i32 i = 0; i < flags.count; i += 1){ - flags.vals[i] = SCu8(models->settings.custom_flags[i]); - } - - Input_Event event = {}; - event.kind = InputEventKind_Core; - event.core.code = CoreCode_Startup; - event.core.flag_strings = flags; - event.core.file_names = file_names; - co_send_event(tctx, models, &event); - } - - // NOTE(allen): consume event stream - Input_Event_Node *input_node = input_list.first; - Input_Event_Node *input_node_next = 0; - for (;; input_node = input_node_next){ - // NOTE(allen): first handle any events coming from the view command - // function queue - Model_View_Command_Function cmd_func = models_pop_view_command_function(models); - if (cmd_func.custom_func != 0){ - View *view = imp_get_view(models, cmd_func.view_id); - if (view != 0){ - input_node_next = input_node; - Input_Event cmd_func_event = {}; - cmd_func_event.kind = InputEventKind_CustomFunction; - cmd_func_event.custom_func = cmd_func.custom_func; - co_send_event(tctx, models, view, &cmd_func_event); - continue; - } - } - - Temp_Memory_Block temp(scratch); - Input_Event *simulated_input = 0; - Input_Event virtual_event = models_pop_virtual_event(scratch, models); - if (virtual_event.kind != InputEventKind_None){ - virtual_event.virtual_event = true; - simulated_input = &virtual_event; - } - else{ - if (input_node == 0){ - break; - } - input_node_next = input_node->next; - simulated_input = &input_node->event; - - if (simulated_input->kind == InputEventKind_TextInsert && simulated_input->text.blocked){ - continue; - } - - // NOTE(allen): record to keyboard history - if (simulated_input->kind == InputEventKind_KeyStroke || - simulated_input->kind == InputEventKind_KeyRelease || - simulated_input->kind == InputEventKind_TextInsert){ - Temp_Memory_Block temp_key_line(scratch); - String_Const_u8 key_line = stringize_keyboard_event(scratch, simulated_input); - output_file_append(tctx, models, models->keyboard_buffer, key_line); - } - } - - b32 event_was_handled = false; - Input_Event *event = simulated_input; - - Panel *active_panel = layout_get_active_panel(layout); - View *view = active_panel->view; - Assert(view != 0); - - switch (models->state){ - case APP_STATE_EDIT: - { - typedef i32 Event_Consume_Rule; - enum{ - EventConsume_None, - EventConsume_BeginResize, - EventConsume_ClickChangeView, - EventConsume_CustomCommand, - }; - - Event_Consume_Rule consume_rule = EventConsume_CustomCommand; - if (match_mouse_code(event, MouseCode_Left) && (divider_panel != 0)){ - consume_rule = EventConsume_BeginResize; - } - else if (match_mouse_code(event, MouseCode_Left) && - mouse_panel != 0 && mouse_panel != active_panel){ - consume_rule = EventConsume_ClickChangeView; - } - - switch (consume_rule){ - case EventConsume_BeginResize: - { - models->state = APP_STATE_RESIZING; - models->resizing_intermediate_panel = divider_panel; - event_was_handled = true; - }break; - - case EventConsume_ClickChangeView: - { - // NOTE(allen): run deactivate command - co_send_core_event(tctx, models, view, CoreCode_ClickDeactivateView); - - layout->active_panel = mouse_panel; - models->animate_next_frame = true; - active_panel = mouse_panel; - view = active_panel->view; - - // NOTE(allen): run activate command - co_send_core_event(tctx, models, view, CoreCode_ClickActivateView); - - event_was_handled = true; - }break; - - case EventConsume_CustomCommand: - { - event_was_handled = co_send_event(tctx, models, view, event); - }break; - } - }break; - - case APP_STATE_RESIZING: - { - Event_Property event_flags = get_event_properties(event); - if (HasFlag(event_flags, EventProperty_AnyKey) || - match_mouse_code_release(event, MouseCode_Left)){ - models->state = APP_STATE_EDIT; - } - else if (event->kind == InputEventKind_MouseMove){ - if (input->mouse.l){ - Panel *split = models->resizing_intermediate_panel; - Range_i32 limits = layout_get_limiting_range_on_split(layout, split); - i32 mouse_position = (split->vertical_split)?(mouse.x):(mouse.y); - mouse_position = clamp(limits.min, mouse_position, limits.max); - layout_set_split_absolute_position(layout, split, mouse_position); - } - else{ - models->state = APP_STATE_EDIT; - } - } - }break; - } - - if (event_was_handled && event->kind == InputEventKind_KeyStroke){ - for (Input_Event *dependent_text = event->key.first_dependent_text; - dependent_text != 0; - dependent_text = dependent_text->text.next_text){ - Assert(dependent_text->kind == InputEventKind_TextInsert); - dependent_text->text.blocked = true; - } - } - } - - linalloc_clear(models->virtual_event_arena); - models->free_virtual_event = 0; - models->first_virtual_event = 0; - models->last_virtual_event = 0; - - // NOTE(allen): send panel size update - if (models->layout.panel_state_dirty){ - models->layout.panel_state_dirty = false; - if (models->buffer_viewer_update != 0){ - Application_Links app = {}; - app.tctx = tctx; - app.cmd_context = models; - models->buffer_viewer_update(&app); - } - } - - // NOTE(allen): dt - f32 literal_dt = 0.f; - u64 now_usecond_stamp = system_now_time(); - if (!input->first_step){ - u64 elapsed_useconds = now_usecond_stamp - models->last_render_usecond_stamp; - literal_dt = (f32)((f64)(elapsed_useconds)/1000000.f); - } - models->last_render_usecond_stamp = now_usecond_stamp; - f32 animation_dt = 0.f; - if (models->animated_last_frame){ - animation_dt = literal_dt; - } - - // NOTE(allen): on the first frame there should be no scrolling - if (input->first_step){ - for (Panel *panel = layout_get_first_open_panel(layout); - panel != 0; - panel = layout_get_next_open_panel(layout, panel)){ - View *view = panel->view; - File_Edit_Positions edit_pos = view_get_edit_pos(view); - edit_pos.scroll.position = view_normalize_buffer_point(tctx, models, view, edit_pos.scroll.target); - block_zero_struct(&edit_pos.scroll.target); - view_set_edit_pos(view, edit_pos); - } - } - - // NOTE(allen): hook for files reloaded - { - Working_Set *working_set = &models->working_set; - Assert(working_set->has_external_mod_sentinel.next != 0); - if (working_set->has_external_mod_sentinel.next != &working_set->has_external_mod_sentinel){ - for (Node *node = working_set->has_external_mod_sentinel.next, *next = 0; - node != &working_set->has_external_mod_sentinel; - node = next){ - next = node->next; - Editing_File *file = CastFromMember(Editing_File, external_mod_node, node); - dll_remove(node); - block_zero_struct(node); - co_send_core_event(tctx, models, CoreCode_FileExternallyModified, file->id); - } - } - } - - // NOTE(allen): if the exit signal has been sent, run the exit hook. - if (input->trying_to_kill){ - models->keep_playing = false; - } - if (!models->keep_playing){ - if (co_send_core_event(tctx, models, CoreCode_TryExit)){ - models->keep_playing = true; - } - } - - // NOTE(allen): rendering - { - Frame_Info frame = {}; - frame.index = models->frame_counter; - frame.literal_dt = literal_dt; - frame.animation_dt = animation_dt; - - Application_Links app = {}; - app.tctx = tctx; - app.cmd_context = models; - - if (models->tick != 0){ - models->tick(&app, frame); - } - - begin_render_section(target, models->frame_counter, literal_dt, animation_dt); - models->in_render_mode = true; - - Live_Views *live_views = &models->view_set; - for (Node *node = layout->open_panels.next; - node != &layout->open_panels; - node = node->next){ - Panel *panel = CastFromMember(Panel, node, node); - View *view = panel->view; - View_Context_Node *ctx = view->ctx; - if (ctx != 0){ - Render_Caller_Function *render_caller = ctx->ctx.render_caller; - if (render_caller != 0){ - render_caller(&app, frame, view_get_id(live_views, view)); - } - } - } - - models->in_render_mode = false; - end_render_section(target); - } - - // NOTE(allen): flush the log - log_flush(tctx, models); - - // NOTE(allen): set the app_result - Application_Step_Result app_result = {}; - app_result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; - app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr; - - // NOTE(allen): get new window title - if (models->has_new_title){ - models->has_new_title = false; - app_result.has_new_title = true; - app_result.title_string = models->title_space; - } - - // NOTE(allen): get cursor type - if (mouse_panel != 0 && !mouse_in_margin){ - app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; - } - else if (divider_panel != 0){ - if (divider_panel->vertical_split){ - app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; - } - else{ - app_result.mouse_cursor_type = APP_MOUSE_CURSOR_UPDOWN; - } - } - else{ - app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; - } - - models->prev_mouse_panel = mouse_panel; - app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr; - app_result.perform_kill = !models->keep_playing; - app_result.animating = models->animate_next_frame; - if (models->animate_next_frame){ - // NOTE(allen): Silence the timer, because we're going to do another frame right away anyways. - system_wake_up_timer_set(models->period_wakeup_timer, max_u32); - } - else{ - // NOTE(allen): Set the timer's wakeup period, possibly to max_u32 thus effectively silencing it. - system_wake_up_timer_set(models->period_wakeup_timer, models->next_animate_delay); - } - - // NOTE(allen): Update Frame to Frame States - models->prev_p = input->mouse.p; - models->animated_last_frame = app_result.animating; - models->frame_counter += 1; - - // end-of-app_step - return(app_result); -} - -extern "C" App_Get_Functions_Sig(app_get_functions){ - App_Functions result = {}; - - result.load_vtables = app_load_vtables; - result.get_logger = app_get_logger; - result.read_command_line = app_read_command_line; - result.init = app_init; - result.step = app_step; - - return(result); -} - -// BOTTOM - +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Application layer for project codename "4ed" + * + */ + +// TOP + +internal void +init_command_line_settings(App_Settings *settings, Plat_Settings *plat_settings, i32 argc, char **argv){ + char *arg = 0; + Command_Line_Mode mode = CLMode_App; + Command_Line_Action action = CLAct_Nothing; + b32 strict = false; + + settings->init_files_max = ArrayCount(settings->init_files); + for (i32 i = 1; i <= argc; ++i){ + if (i == argc){ + arg = ""; + } + else{ + arg = argv[i]; + } + + if (arg[0] == '-' && arg[1] == '-'){ + char *long_arg_name = arg+2; + if (string_match(SCu8(long_arg_name), string_u8_litexpr("custom"))){ + mode = CLMode_Custom; + continue; + } + } + + switch (mode){ + case CLMode_App: + { + switch (action){ + case CLAct_Nothing: + { + if (arg[0] == '-'){ + action = CLAct_Ignore; + switch (arg[1]){ + case 'd': action = CLAct_CustomDLL; strict = false; break; + case 'D': action = CLAct_CustomDLL; strict = true; break; + + case 'i': action = CLAct_InitialFilePosition; break; + + case 'w': action = CLAct_WindowSize; break; + case 'W': action = CLAct_WindowMaximize; break; + case 'p': action = CLAct_WindowPosition; break; + case 'F': action = CLAct_WindowFullscreen; break; + + case 'f': action = CLAct_FontSize; break; + case 'h': action = CLAct_FontUseHinting; --i; break; + } + } + else if (arg[0] != 0){ + if (settings->init_files_count < settings->init_files_max){ + i32 index = settings->init_files_count++; + settings->init_files[index] = arg; + } + } + }break; + + case CLAct_CustomDLL: + { + plat_settings->custom_dll_is_strict = (b8)strict; + if (i < argc){ + plat_settings->custom_dll = argv[i]; + } + action = CLAct_Nothing; + }break; + + case CLAct_InitialFilePosition: + { + if (i < argc){ + settings->initial_line = (i32)string_to_integer(SCu8(argv[i]), 10); + } + action = CLAct_Nothing; + }break; + + case CLAct_WindowSize: + { + if (i + 1 < argc){ + plat_settings->set_window_size = true; + + i32 w = (i32)string_to_integer(SCu8(argv[i]), 10); + i32 h = (i32)string_to_integer(SCu8(argv[i + 1]), 10); + if (w > 0){ + plat_settings->window_w = w; + } + if (h > 0){ + plat_settings->window_h = h; + } + + ++i; + } + action = CLAct_Nothing; + }break; + + case CLAct_WindowMaximize: + { + --i; + plat_settings->maximize_window = true; + action = CLAct_Nothing; + }break; + + case CLAct_WindowPosition: + { + if (i + 1 < argc){ + plat_settings->set_window_pos = true; + + i32 x = (i32)string_to_integer(SCu8(argv[i]), 10); + i32 y = (i32)string_to_integer(SCu8(argv[i + 1]), 10); + if (x > 0){ + plat_settings->window_x = x; + } + if (y > 0){ + plat_settings->window_y = y; + } + + ++i; + } + action = CLAct_Nothing; + }break; + + case CLAct_WindowFullscreen: + { + --i; + plat_settings->fullscreen_window = true; + action = CLAct_Nothing; + }break; + + case CLAct_FontSize: + { + if (i < argc){ + settings->font_size = (i32)string_to_integer(SCu8(argv[i]), 10); + } + action = CLAct_Nothing; + }break; + + case CLAct_FontUseHinting: + { + plat_settings->use_hinting = true; + settings->use_hinting = plat_settings->use_hinting; + action = CLAct_Nothing; + }break; + } + }break; + + case CLMode_Custom: + { + settings->custom_flags = argv + i; + settings->custom_flags_count = argc - i; + i = argc; + mode = CLMode_App; + }break; + } + } +} + +//////////////////////////////// + +internal Models* +models_init(void){ + Arena arena = make_arena_system(); + Models *models = push_array_zero(&arena, Models, 1); + models->arena_ = arena; + models->arena = &models->arena_; + heap_init(&models->heap, get_base_allocator_system()); + return(models); +} + +internal void +app_load_vtables(API_VTable_system *vtable_system, API_VTable_font *vtable_font, API_VTable_graphics *vtable_graphics){ + system_api_read_vtable(vtable_system); + font_api_read_vtable(vtable_font); + graphics_api_read_vtable(vtable_graphics); +} + +internal Log_Function* +app_get_logger(void){ + log_init(); + return(log_string); +} + +App_Read_Command_Line_Sig(app_read_command_line){ + Models *models = models_init(); + App_Settings *settings = &models->settings; + block_zero_struct(settings); + if (argc > 1){ + init_command_line_settings(&models->settings, plat_settings, argc, argv); + } + *files = models->settings.init_files; + *file_count = &models->settings.init_files_count; + return(models); +} + +App_Init_Sig(app_init){ + Models *models = (Models*)base_ptr; + models->keep_playing = true; + + models->config_api = api; + models->virtual_event_arena = reserve_arena(tctx); + + profile_init(&models->profile_list); + + managed_ids_init(tctx->allocator, &models->managed_id_set); + + API_VTable_custom custom_vtable = {}; + custom_api_fill_vtable(&custom_vtable); + API_VTable_system system_vtable = {}; + system_api_fill_vtable(&system_vtable); + Custom_Layer_Init_Type *custom_init = api.init_apis(&custom_vtable, &system_vtable); + Assert(custom_init != 0); + + // NOTE(allen): coroutines + coroutine_system_init(&models->coroutines); + + // NOTE(allen): font set + font_set_init(&models->font_set); + + // NOTE(allen): live set + Arena *arena = models->arena; + { + models->view_set.count = 0; + models->view_set.max = MAX_VIEWS; + models->view_set.views = push_array(arena, View, models->view_set.max); + + //dll_init_sentinel + models->view_set.free_sentinel.next = &models->view_set.free_sentinel; + models->view_set.free_sentinel.prev = &models->view_set.free_sentinel; + + i32 max = models->view_set.max; + View *view = models->view_set.views; + for (i32 i = 0; i < max; ++i, ++view){ + //dll_insert(&models->view_set.free_sentinel, view); + view->next = models->view_set.free_sentinel.next; + view->prev = &models->view_set.free_sentinel; + models->view_set.free_sentinel.next = view; + view->next->prev = view; + } + } + + lifetime_allocator_init(tctx->allocator, &models->lifetime_allocator); + dynamic_workspace_init(&models->lifetime_allocator, DynamicWorkspace_Global, 0, &models->dynamic_workspace); + + // NOTE(allen): file setup + working_set_init(models, &models->working_set); + Mutex_Lock file_order_lock(models->working_set.mutex); + + // NOTE(allen): + global_history_init(&models->global_history); + text_layout_init(tctx, &models->text_layouts); + + // NOTE(allen): 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): do(better clipboard allocation) + if (clipboard.str != 0){ + String_Const_u8 *dest = working_set_next_clipboard_string(&models->heap, &models->working_set, clipboard.size); + block_copy(dest->str, clipboard.str, clipboard.size); + } + + // NOTE(allen): style setup + { + Scratch_Block scratch(tctx); + Face_Description description = {}; + description.font.file_name = get_file_path_in_fonts_folder(scratch, string_u8_litexpr("liberation-mono.ttf")); + description.parameters.pt_size = 12; + Face *new_face = font_set_new_face(&models->font_set, &description); + models->global_face_id = new_face->id; + } + + // NOTE(allen): title space + models->has_new_title = true; + models->title_capacity = KB(4); + models->title_space = push_array(arena, char, models->title_capacity); + block_copy(models->title_space, WINDOW_NAME, sizeof(WINDOW_NAME)); + + // NOTE(allen): miscellaneous init + hot_directory_init(arena, &models->hot_directory, current_directory); + child_process_container_init(tctx->allocator, &models->child_processes); + models->period_wakeup_timer = system_wake_up_timer_create(); + + // NOTE(allen): custom layer init + Application_Links app = {}; + app.tctx = tctx; + app.cmd_context = models; + custom_init(&app); + + // NOTE(allen): init baked in buffers + File_Init init_files[] = { + { string_u8_litinit("*messages*"), &models->message_buffer , true , }, + { string_u8_litinit("*scratch*") , &models->scratch_buffer , false, }, + { string_u8_litinit("*log*") , &models->log_buffer , true , }, + { string_u8_litinit("*keyboard*"), &models->keyboard_buffer, true , }, + }; + + Heap *heap = &models->heap; + for (i32 i = 0; i < ArrayCount(init_files); ++i){ + Editing_File *file = working_set_allocate_file(&models->working_set, &models->lifetime_allocator); + buffer_bind_name(tctx, models, arena, &models->working_set, file, init_files[i].name); + + if (init_files[i].ptr != 0){ + *init_files[i].ptr = file; + } + + File_Attributes attributes = {}; + file_create_from_string(tctx, models, file, SCu8(), attributes); + if (init_files[i].read_only){ + file->settings.read_only = true; + history_free(tctx, &file->state.history); + } + + file->settings.never_kill = true; + file_set_unimportant(file, true); + } + + // NOTE(allen): setup first panel + { + Panel *panel = layout_initialize(arena, &models->layout); + View *new_view = live_set_alloc_view(&models->lifetime_allocator, &models->view_set, panel); + view_init(tctx, models, new_view, models->scratch_buffer, models->view_event_handler); + } +} + +App_Step_Sig(app_step){ + Models *models = (Models*)base_ptr; + + Mutex_Lock file_order_lock(models->working_set.mutex); + Scratch_Block scratch(tctx, Scratch_Share); + + models->next_animate_delay = max_u32; + models->animate_next_frame = false; + + // NOTE(allen): per-frame update of models state + begin_frame(target, &models->font_set); + models->target = target; + models->input = input; + + // NOTE(allen): OS clipboard event handling + String_Const_u8 clipboard = input->clipboard; + if (clipboard.str != 0){ + String_Const_u8 *dest = working_set_next_clipboard_string(&models->heap, &models->working_set, clipboard.size); + dest->size = eol_convert_in((char*)dest->str, (char*)clipboard.str, (i32)clipboard.size); + if (input->clipboard_changed){ + co_send_core_event(tctx, models, CoreCode_NewClipboardContents, *dest); + } + } + + // NOTE(allen): reorganizing panels on screen + Vec2_i32 prev_dim = layout_get_root_size(&models->layout); + Vec2_i32 current_dim = V2i32(target->width, target->height); + layout_set_root_size(&models->layout, current_dim); + + // NOTE(allen): update child processes + f32 dt = input->dt; + if (dt > 0){ + Temp_Memory_Block temp(scratch); + + Child_Process_Container *child_processes = &models->child_processes; + Child_Process **processes_to_free = push_array(scratch, Child_Process*, child_processes->active_child_process_count); + i32 processes_to_free_count = 0; + + u32 max = KB(128); + char *dest = push_array(scratch, char, max); + + for (Node *node = child_processes->child_process_active_list.next; + node != &child_processes->child_process_active_list; + node = node->next){ + Child_Process *child_process = CastFromMember(Child_Process, node, node); + + Editing_File *file = child_process->out_file; + CLI_Handles *cli = &child_process->cli; + + // TODO(allen): do(call a 'child process updated hook' let that hook populate the buffer if it so chooses) + + b32 edited_file = false; + u32 amount = 0; + system_cli_begin_update(cli); + if (system_cli_update_step(cli, dest, max, &amount)){ + if (file != 0 && amount > 0){ + amount = eol_in_place_convert_in(dest, amount); + output_file_append(tctx, models, file, SCu8(dest, amount)); + edited_file = true; + } + } + + if (system_cli_end_update(cli)){ + if (file != 0){ + String_Const_u8 str = push_u8_stringf(scratch, "exited with code %d", cli->exit); + output_file_append(tctx, models, file, str); + edited_file = true; + } + processes_to_free[processes_to_free_count++] = child_process; + child_process_set_return_code(models, child_processes, child_process->id, cli->exit); + } + + if (child_process->cursor_at_end && file != 0){ + file_cursor_to_end(tctx, models, file); + } + } + + for (i32 i = 0; i < processes_to_free_count; ++i){ + child_process_free(child_processes, processes_to_free[i]->id); + } + } + + // NOTE(allen): simulated events + Input_List input_list = input->events; + Input_Modifier_Set modifiers = system_get_keyboard_modifiers(scratch); + if (input->mouse.press_l){ + Input_Event event = {}; + event.kind = InputEventKind_MouseButton; + event.mouse.code = MouseCode_Left; + event.mouse.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + else if (input->mouse.release_l){ + Input_Event event = {}; + event.kind = InputEventKind_MouseButtonRelease; + event.mouse.code = MouseCode_Left; + event.mouse.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + if (input->mouse.press_r){ + Input_Event event = {}; + event.kind = InputEventKind_MouseButton; + event.mouse.code = MouseCode_Right; + event.mouse.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + else if (input->mouse.release_r){ + Input_Event event = {}; + event.kind = InputEventKind_MouseButtonRelease; + event.mouse.code = MouseCode_Right; + event.mouse.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + if (input->mouse.wheel != 0){ + Input_Event event = {}; + event.kind = InputEventKind_MouseWheel; + event.mouse_wheel.value = (f32)(input->mouse.wheel); + event.mouse_wheel.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + if (input->mouse.p != models->prev_p){ + b32 was_in_window = rect_contains_point(Ri32(0, 0, prev_dim.x, prev_dim.y), models->prev_p); + b32 is_in_window = rect_contains_point(Ri32(0, 0, current_dim.x, current_dim.y), input->mouse.p); + if (is_in_window || was_in_window){ + Input_Event event = {}; + event.kind = InputEventKind_MouseMove; + event.mouse_move.p = input->mouse.p; + event.mouse.modifiers = copy_modifier_set(scratch, &modifiers); + push_input_event(scratch, &input_list, &event); + } + } + if (models->animated_last_frame){ + Input_Event event = {}; + event.kind = InputEventKind_Core; + event.core.code = CoreCode_Animate; + push_input_event(scratch, &input_list, &event); + } + + // NOTE(allen): expose layout + Layout *layout = &models->layout; + + // NOTE(allen): mouse hover status + Panel *mouse_panel = 0; + Panel *divider_panel = 0; + b32 mouse_in_margin = false; + Vec2_i32 mouse = input->mouse.p; + { + for (Panel *panel = layout_get_first_open_panel(layout); + panel != 0; + panel = layout_get_next_open_panel(layout, panel)){ + if (rect_contains_point(panel->rect_full, mouse)){ + mouse_panel = panel; + if (!rect_contains_point(panel->rect_inner, mouse)){ + mouse_in_margin = true; + for (divider_panel = mouse_panel->parent; + divider_panel != 0; + divider_panel = divider_panel->parent){ + if (rect_contains_point(divider_panel->rect_inner, mouse)){ + break; + } + } + } + } + if (mouse_panel != 0){ + break; + } + } + } + + // NOTE(allen): First frame initialization + if (input->first_step){ + Temp_Memory_Block temp(scratch); + + String_Const_u8_Array file_names = {}; + file_names.count = models->settings.init_files_count; + file_names.vals = push_array(scratch, String_Const_u8, file_names.count); + for (i32 i = 0; i < file_names.count; i += 1){ + file_names.vals[i] = SCu8(models->settings.init_files[i]); + } + + String_Const_u8_Array flags = {}; + flags.count = models->settings.custom_flags_count; + flags.vals = push_array(scratch, String_Const_u8, flags.count); + for (i32 i = 0; i < flags.count; i += 1){ + flags.vals[i] = SCu8(models->settings.custom_flags[i]); + } + + Input_Event event = {}; + event.kind = InputEventKind_Core; + event.core.code = CoreCode_Startup; + event.core.flag_strings = flags; + event.core.file_names = file_names; + co_send_event(tctx, models, &event); + } + + // NOTE(allen): consume event stream + Input_Event_Node *input_node = input_list.first; + Input_Event_Node *input_node_next = 0; + for (;; input_node = input_node_next){ + // NOTE(allen): first handle any events coming from the view command + // function queue + Model_View_Command_Function cmd_func = models_pop_view_command_function(models); + if (cmd_func.custom_func != 0){ + View *view = imp_get_view(models, cmd_func.view_id); + if (view != 0){ + input_node_next = input_node; + Input_Event cmd_func_event = {}; + cmd_func_event.kind = InputEventKind_CustomFunction; + cmd_func_event.custom_func = cmd_func.custom_func; + co_send_event(tctx, models, view, &cmd_func_event); + continue; + } + } + + Temp_Memory_Block temp(scratch); + Input_Event *simulated_input = 0; + Input_Event virtual_event = models_pop_virtual_event(scratch, models); + if (virtual_event.kind != InputEventKind_None){ + virtual_event.virtual_event = true; + simulated_input = &virtual_event; + } + else{ + if (input_node == 0){ + break; + } + input_node_next = input_node->next; + simulated_input = &input_node->event; + + if (simulated_input->kind == InputEventKind_TextInsert && simulated_input->text.blocked){ + continue; + } + + // NOTE(allen): record to keyboard history + if (simulated_input->kind == InputEventKind_KeyStroke || + simulated_input->kind == InputEventKind_KeyRelease || + simulated_input->kind == InputEventKind_TextInsert){ + Temp_Memory_Block temp_key_line(scratch); + String_Const_u8 key_line = stringize_keyboard_event(scratch, simulated_input); + output_file_append(tctx, models, models->keyboard_buffer, key_line); + } + } + + b32 event_was_handled = false; + Input_Event *event = simulated_input; + + Panel *active_panel = layout_get_active_panel(layout); + View *view = active_panel->view; + Assert(view != 0); + + switch (models->state){ + case APP_STATE_EDIT: + { + typedef i32 Event_Consume_Rule; + enum{ + EventConsume_None, + EventConsume_BeginResize, + EventConsume_ClickChangeView, + EventConsume_CustomCommand, + }; + + Event_Consume_Rule consume_rule = EventConsume_CustomCommand; + if (match_mouse_code(event, MouseCode_Left) && (divider_panel != 0)){ + consume_rule = EventConsume_BeginResize; + } + else if (match_mouse_code(event, MouseCode_Left) && + mouse_panel != 0 && mouse_panel != active_panel){ + consume_rule = EventConsume_ClickChangeView; + } + + switch (consume_rule){ + case EventConsume_BeginResize: + { + models->state = APP_STATE_RESIZING; + models->resizing_intermediate_panel = divider_panel; + event_was_handled = true; + }break; + + case EventConsume_ClickChangeView: + { + // NOTE(allen): run deactivate command + co_send_core_event(tctx, models, view, CoreCode_ClickDeactivateView); + + layout->active_panel = mouse_panel; + models->animate_next_frame = true; + active_panel = mouse_panel; + view = active_panel->view; + + // NOTE(allen): run activate command + co_send_core_event(tctx, models, view, CoreCode_ClickActivateView); + + event_was_handled = true; + }break; + + case EventConsume_CustomCommand: + { + event_was_handled = co_send_event(tctx, models, view, event); + }break; + } + }break; + + case APP_STATE_RESIZING: + { + Event_Property event_flags = get_event_properties(event); + if (HasFlag(event_flags, EventProperty_AnyKey) || + match_mouse_code_release(event, MouseCode_Left)){ + models->state = APP_STATE_EDIT; + } + else if (event->kind == InputEventKind_MouseMove){ + if (input->mouse.l){ + Panel *split = models->resizing_intermediate_panel; + Range_i32 limits = layout_get_limiting_range_on_split(layout, split); + i32 mouse_position = (split->vertical_split)?(mouse.x):(mouse.y); + mouse_position = clamp(limits.min, mouse_position, limits.max); + layout_set_split_absolute_position(layout, split, mouse_position); + } + else{ + models->state = APP_STATE_EDIT; + } + } + }break; + } + + if (event_was_handled && event->kind == InputEventKind_KeyStroke){ + for (Input_Event *dependent_text = event->key.first_dependent_text; + dependent_text != 0; + dependent_text = dependent_text->text.next_text){ + Assert(dependent_text->kind == InputEventKind_TextInsert); + dependent_text->text.blocked = true; + } + } + } + + linalloc_clear(models->virtual_event_arena); + models->free_virtual_event = 0; + models->first_virtual_event = 0; + models->last_virtual_event = 0; + + // NOTE(allen): send panel size update + if (models->layout.panel_state_dirty){ + models->layout.panel_state_dirty = false; + if (models->buffer_viewer_update != 0){ + Application_Links app = {}; + app.tctx = tctx; + app.cmd_context = models; + models->buffer_viewer_update(&app); + } + } + + // NOTE(allen): dt + f32 literal_dt = 0.f; + u64 now_usecond_stamp = system_now_time(); + if (!input->first_step){ + u64 elapsed_useconds = now_usecond_stamp - models->last_render_usecond_stamp; + literal_dt = (f32)((f64)(elapsed_useconds)/1000000.f); + } + models->last_render_usecond_stamp = now_usecond_stamp; + f32 animation_dt = 0.f; + if (models->animated_last_frame){ + animation_dt = literal_dt; + } + + // NOTE(allen): on the first frame there should be no scrolling + if (input->first_step){ + for (Panel *panel = layout_get_first_open_panel(layout); + panel != 0; + panel = layout_get_next_open_panel(layout, panel)){ + View *view = panel->view; + File_Edit_Positions edit_pos = view_get_edit_pos(view); + edit_pos.scroll.position = view_normalize_buffer_point(tctx, models, view, edit_pos.scroll.target); + block_zero_struct(&edit_pos.scroll.target); + view_set_edit_pos(view, edit_pos); + } + } + + // NOTE(allen): hook for files reloaded + { + Working_Set *working_set = &models->working_set; + Assert(working_set->has_external_mod_sentinel.next != 0); + if (working_set->has_external_mod_sentinel.next != &working_set->has_external_mod_sentinel){ + for (Node *node = working_set->has_external_mod_sentinel.next, *next = 0; + node != &working_set->has_external_mod_sentinel; + node = next){ + next = node->next; + Editing_File *file = CastFromMember(Editing_File, external_mod_node, node); + dll_remove(node); + block_zero_struct(node); + co_send_core_event(tctx, models, CoreCode_FileExternallyModified, file->id); + } + } + } + + // NOTE(allen): if the exit signal has been sent, run the exit hook. + if (input->trying_to_kill){ + models->keep_playing = false; + } + if (!models->keep_playing){ + if (co_send_core_event(tctx, models, CoreCode_TryExit)){ + models->keep_playing = true; + } + } + + // NOTE(allen): rendering + { + Frame_Info frame = {}; + frame.index = models->frame_counter; + frame.literal_dt = literal_dt; + frame.animation_dt = animation_dt; + + Application_Links app = {}; + app.tctx = tctx; + app.cmd_context = models; + + if (models->tick != 0){ + models->tick(&app, frame); + } + + begin_render_section(target, models->frame_counter, literal_dt, animation_dt); + models->in_render_mode = true; + + Live_Views *live_views = &models->view_set; + for (Node *node = layout->open_panels.next; + node != &layout->open_panels; + node = node->next){ + Panel *panel = CastFromMember(Panel, node, node); + View *view = panel->view; + View_Context_Node *ctx = view->ctx; + if (ctx != 0){ + Render_Caller_Function *render_caller = ctx->ctx.render_caller; + if (render_caller != 0){ + render_caller(&app, frame, view_get_id(live_views, view)); + } + } + } + + models->in_render_mode = false; + end_render_section(target); + } + + // NOTE(allen): flush the log + log_flush(tctx, models); + + // NOTE(allen): set the app_result + Application_Step_Result app_result = {}; + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; + app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr; + + // NOTE(allen): get new window title + if (models->has_new_title){ + models->has_new_title = false; + app_result.has_new_title = true; + app_result.title_string = models->title_space; + } + + // NOTE(allen): get cursor type + if (mouse_panel != 0 && !mouse_in_margin){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + } + else if (divider_panel != 0){ + if (divider_panel->vertical_split){ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT; + } + else{ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_UPDOWN; + } + } + else{ + app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW; + } + + models->prev_mouse_panel = mouse_panel; + app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr; + app_result.perform_kill = !models->keep_playing; + app_result.animating = models->animate_next_frame; + if (models->animate_next_frame){ + // NOTE(allen): Silence the timer, because we're going to do another frame right away anyways. + system_wake_up_timer_set(models->period_wakeup_timer, max_u32); + } + else{ + // NOTE(allen): Set the timer's wakeup period, possibly to max_u32 thus effectively silencing it. + system_wake_up_timer_set(models->period_wakeup_timer, models->next_animate_delay); + } + + // NOTE(allen): Update Frame to Frame States + models->prev_p = input->mouse.p; + models->animated_last_frame = app_result.animating; + models->frame_counter += 1; + + // end-of-app_step + return(app_result); +} + +extern "C" App_Get_Functions_Sig(app_get_functions){ + App_Functions result = {}; + + result.load_vtables = app_load_vtables; + result.get_logger = app_get_logger; + result.read_command_line = app_read_command_line; + result.init = app_init; + result.step = app_step; + + return(result); +} + +// BOTTOM + diff --git a/custom/4coder_fancy.cpp b/custom/4coder_fancy.cpp index 9b840962..4364db02 100644 --- a/custom/4coder_fancy.cpp +++ b/custom/4coder_fancy.cpp @@ -347,11 +347,11 @@ function Fancy_String* push_fancy_string_fixed(Arena *arena, Fancy_Line *line, FColor fore, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fore, 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fore, 0.f, 0.f, "%-*.*s", max, string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fore, 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fore, 0.f, 0.f, "%-*.*s...", max - 3, string_expand(value))); } } @@ -360,12 +360,12 @@ push_fancy_string_fixed(Arena *arena, Fancy_Line *line, f32 pre_margin, f32 post_margin, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), pre_margin, post_margin, "%-*.*s", max, string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), pre_margin, post_margin, "%-*.*s...", max - 3, string_expand(value))); } @@ -374,11 +374,11 @@ function Fancy_String* push_fancy_string_fixed(Arena *arena, Fancy_Line *line, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), 0.f, 0.f, "%-*.*s", max, string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), 0.f, 0.f, "%-*.*s...", max - 3, string_expand(value))); } } @@ -452,11 +452,11 @@ function Fancy_String* push_fancy_string_trunc(Arena *arena, Fancy_Line *line, FColor fore, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fore, 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fore, 0.f, 0.f, "%.*s", string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fore, 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fore, 0.f, 0.f, "%.*s...", max - 3, value.str)); } } @@ -465,12 +465,12 @@ push_fancy_string_trunc(Arena *arena, Fancy_Line *line, f32 pre_margin, f32 post_margin, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), pre_margin, post_margin, "%.*s", string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), pre_margin, post_margin, "%.*s...", max - 3, value.str)); } @@ -479,11 +479,11 @@ function Fancy_String* push_fancy_string_trunc(Arena *arena, Fancy_Line *line, String_Const_u8 value, i32 max){ if (value.size <= max){ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), 0.f, 0.f, "%.*s", string_expand(value))); } else{ - return(push_fancy_stringf(arena, line, 0, fcolor_zero(), 0.f, 0.f, + return(push_fancy_stringf(arena, line, (Face_ID)0, fcolor_zero(), 0.f, 0.f, "%.*s...", max - 3, value.str)); } } @@ -620,7 +620,7 @@ draw_fancy_string__inner(Application_Links *app, Face_ID face, FColor fore, Fanc use_fore = string->fore; } if (use_face != 0){ - ARGB_Color use_argb = fcolor_resolve(use_fore); + ARGB_Color use_argb = fcolor_resolve(use_fore); Face_Metrics metrics = get_face_metrics(app, use_face); f32 down_shift = (base_line - metrics.ascent); down_shift = clamp_bot(0.f, down_shift); @@ -668,7 +668,7 @@ get_fancy_string_height(Application_Links *app, Face_ID face, function f32 get_fancy_string_text_height(Application_Links *app, Face_ID face, - Fancy_String *string){ + Fancy_String *string){ Fancy_String *next = string->next; string->next = 0; f32 result = get_fancy_string_text_height__inner(app, face, string); @@ -700,10 +700,10 @@ function f32 get_fancy_line_width(Application_Links *app, Face_ID face, Fancy_Line *line){ f32 result = 0.f; if (line != 0){ - if (line->face != 0){ - face = line->face; - } - result = get_fancy_string_width__inner(app, face, line->first); + if (line->face != 0){ + face = line->face; + } + result = get_fancy_string_width__inner(app, face, line->first); } return(result); } @@ -749,12 +749,12 @@ draw_fancy_line(Application_Links *app, Face_ID face, FColor fore, Fancy_Line *line, Vec2_f32 p, u32 flags, Vec2_f32 delta){ Vec2_f32 result = {}; if (line != 0){ - if (line->face != 0){ - face = line->face; - } - if (fcolor_is_valid(line->fore)){ - fore = line->fore; - } + if (line->face != 0){ + face = line->face; + } + if (fcolor_is_valid(line->fore)){ + fore = line->fore; + } result = draw_fancy_string__inner(app, face, fore, line->first, p, flags, delta); } return(result); diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 9aab846a..5cace252 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -131,7 +131,6 @@ float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd)); shape_value *= has_thickness; float4 out_color = float4(in.color.xyz, in.color.a * (sample_value + shape_value)); -//float4 out_color = float4(1, 1, 1, shape_value); return(out_color); } )"; @@ -274,9 +273,12 @@ metal__make_buffer(u32 size, id device){ } - (void)drawInMTKView:(nonnull MTKView*)view{ +#if FRED_INTERNAL [capture_scope beginScope]; +#endif + + // HACK(yuval): This is the best way I found to force valid width and height without drawing on the next draw cycle (1 frame delay). - // HACK(yuval): This is the best way I found to force valid width and height without drawing on the next drawing cycle (1 frame delay). CGSize drawable_size = [view drawableSize]; i32 width = (i32)Min(_target->width, drawable_size.width); i32 height = (i32)Min(_target->height, drawable_size.height); @@ -293,8 +295,8 @@ metal__make_buffer(u32 size, id device){ render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f); // NOTE(yuval): Create the render command encoder - id render_encoder - = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + id render_encoder = + [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; render_encoder.label = @"4coder Render Encoder"; // NOTE(yuval): Set the region of the drawable to draw into @@ -303,7 +305,7 @@ metal__make_buffer(u32 size, id device){ // NOTE(yuval): Set the render pipeline to use for drawing [render_encoder setRenderPipelineState:pipeline_state]; - // NOTE(yuval): Calculate and pass in the projection matrix + // NOTE(yuval): Calculate the projection matrix float left = 0, right = (float)width; float bottom = (float)height, top = 0; float near_depth = -1.0f, far_depth = 1.0f; @@ -325,6 +327,8 @@ metal__make_buffer(u32 size, id device){ u32 vertex_buffer_size = (all_vertex_count * sizeof(Render_Vertex)); + printf("Vertices to render: %d\n", all_vertex_count); + // NOTE(yuval): Find & Get a vertex buffer matching the required size Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size]; @@ -338,6 +342,8 @@ metal__make_buffer(u32 size, id device){ length:sizeof(proj) atIndex:1]; + u32 group_count = 0; + u32 buffer_offset = 0; for (Render_Group *group = _target->group_first; group; @@ -346,7 +352,6 @@ metal__make_buffer(u32 size, id device){ { Rect_i32 box = Ri32(group->clip_box); - NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), width - 1); NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), width); NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), height - 1); @@ -405,8 +410,12 @@ metal__make_buffer(u32 size, id device){ buffer_offset += (vertex_count * sizeof(Render_Vertex)); } + + ++group_count; } + printf("Group Count: %u\n", group_count); + [render_encoder endEncoding]; // NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable @@ -422,7 +431,9 @@ metal__make_buffer(u32 size, id device){ // NOTE(yuval): Finalize rendering here and push the command buffer to the GPU [command_buffer commit]; +#if FRED_INTERNAL [capture_scope endScope]; +#endif } - (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind{ diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 00735e43..0925f962 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -315,6 +315,22 @@ mac_error_box(char *msg, b32 shutdown = true){ //////////////////////////////// +#if defined(FRED_INTERNAL) +function inline void +mac_profile(char *name, u64 begin, u64 end){ + printf("%s Time: %fs\n", name, ((end - begin) / 1000000.0f)); +} + +#define MacProfileScope(name) for (u64 glue(_i_, __LINE__) = 0, glue(_begin_, __LINE__) = system_now_time();\ +glue(_i_, __LINE__) == 0;\ +glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_time())) +#else +# define mac_profile(...) +# define MacProfileScope(...) +#endif + +//////////////////////////////// + #import "mac_4ed_renderer.mm" #include "4ed_font_provider_freetype.h" @@ -545,21 +561,6 @@ mac_toggle_fullscreen(void){ //////////////////////////////// -#if defined(FRED_INTERNAL) -function inline void -mac_profile(char *name, u64 begin, u64 end){ - printf("%s Time: %fs\n", name, ((end - begin) / 1000000.0f)); -} - -#define MacProfileScope(name) for (u64 glue(_i_, __LINE__) = 0, glue(_begin_, __LINE__) = system_now_time();\ -glue(_i_, __LINE__) == 0;\ -glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_time())) -#else -# define MacProfileScope(...) -#endif - -//////////////////////////////// - @implementation FCoder_App_Delegate - (void)applicationDidFinishLaunching:(id)sender{ } @@ -628,7 +629,12 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t mac_resize(mac_vars.window); } -- (void)drawRect:(NSRect)bounds{ +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)updateLayer{ u64 prev_timer_start; MacProfileScope("Draw Rect"){ @@ -840,7 +846,9 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } mac_profile("Frame", prev_timer_start, mac_vars.timer_start); +#if FRED_INTERNAL printf("\n"); +#endif } - (BOOL)acceptsFirstResponder{ @@ -856,8 +864,6 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } - (void)keyDown:(NSEvent*)event{ - printf("Key Down: %#X\n", [event keyCode]); - // NOTE(yuval): Process keyboard event [self process_keyboard_event:event down:true]; @@ -896,7 +902,6 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } - (void)keyUp:(NSEvent*)event{ - printf("Key Up: %#X\n", [event keyCode]); [self process_keyboard_event:event down:false]; } @@ -988,7 +993,7 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t } - (void)request_display{ - printf("Display Requested!\n"); + //printf("Display Requested!\n"); CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; @@ -1068,6 +1073,10 @@ glue(_i_, __LINE__) = 1, mac_profile(name, glue(_begin_, __LINE__), system_now_t Vec2_i32 new_m = V2i32(backing_location.x, mac_vars.height - backing_location.y); if (new_m != mac_vars.input_chunk.pers.mouse){ mac_vars.input_chunk.pers.mouse = new_m; + + Rect_i32 screen = Ri32(0, 0, target.width, target.height); + mac_vars.input_chunk.trans.out_of_window = !rect_contains_point(screen, new_m); + } system_signal_step(0); @@ -1270,6 +1279,7 @@ main(int arg_count, char **args){ mac_vars.view = [[FCoder_View alloc] init]; [mac_vars.view setFrame:[content_view bounds]]; [mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + mac_vars.view.wantsLayer = true; // NOTE(yuval): Display window and view [content_view addSubview:mac_vars.view]; diff --git a/platform_mac/mac_4ed_functions.mm b/platform_mac/mac_4ed_functions.mm index 1502a441..85a6c0f4 100644 --- a/platform_mac/mac_4ed_functions.mm +++ b/platform_mac/mac_4ed_functions.mm @@ -844,7 +844,9 @@ system_memory_annotation_sig(){ for (Memory_Annotation_Tracker_Node *node = memory_tracker.first; node != 0; node = node->next){ - Memory_Annotation_Node *r_node = push_array(arena, Memory_Annotation_Node, 1); + // TODO(yuval): Fix the API so that annotations would not mess with the system memory. + // Memory_Annotation_Node *r_node = push_array(arena, Memory_Annotation_Node, 1); + Memory_Annotation_Node *r_node = (Memory_Annotation_Node*)malloc(sizeof(Memory_Annotation_Node)); sll_queue_push(result.first, result.last, r_node); result.count += 1; diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 34fbbeca..19ea4459 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -15,8 +15,6 @@ struct Mac_Metal{ function mac_render_sig(mac_metal__render){ - printf("Rendering using Metal!\n"); - Mac_Metal *metal = (Mac_Metal*)renderer; [metal->view draw]; } @@ -77,8 +75,6 @@ mac_metal__init(NSWindow *window, Render_Target *target){ // TODO(yuval): This function should be exported to a DLL function mac_load_renderer_sig(mac_load_metal_renderer){ - printf("Loding The Metal Renderer!\n"); - Mac_Renderer *renderer = (Mac_Renderer*)mac_metal__init(window, target); return(renderer); } From 1ab40ff5b96bd8acfdb4821ed00b000adb041cad Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 14 Jan 2020 17:21:03 +0200 Subject: [PATCH 63/67] Lowered view sample count to improve the rendering speed on high dpi displays. --- metal/4ed_metal_render.mm | 1 - platform_mac/mac_4ed_metal.mm | 1 - 2 files changed, 2 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 5cace252..7ac6f35e 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -223,7 +223,6 @@ metal__make_buffer(u32 size, id device){ pipeline_state_descriptor.vertexFunction = vertex_function; pipeline_state_descriptor.fragmentFunction = fragment_function; pipeline_state_descriptor.vertexDescriptor = vertexDescriptor; - pipeline_state_descriptor.sampleCount = mtk_view.sampleCount; pipeline_state_descriptor.colorAttachments[0].pixelFormat = mtk_view.colorPixelFormat; pipeline_state_descriptor.colorAttachments[0].blendingEnabled = YES; pipeline_state_descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 19ea4459..0ce3397d 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -56,7 +56,6 @@ mac_metal__init(NSWindow *window, Render_Target *target){ [metal->view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [metal->view setPaused:YES]; [metal->view setEnableSetNeedsDisplay:NO]; - [metal->view setSampleCount:4]; metal->view.device = MTLCreateSystemDefaultDevice(); From 93494bd3984c1675bf3c33b936404208e5ac16d6 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Tue, 14 Jan 2020 17:25:13 +0200 Subject: [PATCH 64/67] Cleanup to the macOS platform layer. --- metal/4ed_metal_render.mm | 8 -------- platform_mac/mac_4ed.mm | 2 +- platform_mac/mac_4ed_opengl.mm | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index 7ac6f35e..6f029cc7 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -326,8 +326,6 @@ metal__make_buffer(u32 size, id device){ u32 vertex_buffer_size = (all_vertex_count * sizeof(Render_Vertex)); - printf("Vertices to render: %d\n", all_vertex_count); - // NOTE(yuval): Find & Get a vertex buffer matching the required size Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size]; @@ -341,8 +339,6 @@ metal__make_buffer(u32 size, id device){ length:sizeof(proj) atIndex:1]; - u32 group_count = 0; - u32 buffer_offset = 0; for (Render_Group *group = _target->group_first; group; @@ -409,12 +405,8 @@ metal__make_buffer(u32 size, id device){ buffer_offset += (vertex_count * sizeof(Render_Vertex)); } - - ++group_count; } - printf("Group Count: %u\n", group_count); - [render_encoder endEncoding]; // NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 0925f962..2318c698 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1286,7 +1286,7 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - renderer = mac_init_renderer(MacRenderer_Metal, mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); mac_resize(w, h); diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index 57dd3348..7efb31c0 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -87,8 +87,6 @@ struct Mac_OpenGL{ NSOpenGLPFAColorSize, 32, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADepthSize, 24, - NSOpenGLPFASampleBuffers, 1, - NSOpenGLPFASamples, 16, 0 }; From ab486eb22947ea3566db8bee94897aa9951f2e12 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Wed, 15 Jan 2020 02:21:02 +0200 Subject: [PATCH 65/67] Removed some debug prints. --- custom/bin/buildsuper_x64-mac.sh | 2 +- platform_mac/mac_4ed.mm | 24 +----------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/custom/bin/buildsuper_x64-mac.sh b/custom/bin/buildsuper_x64-mac.sh index 9f6bce10..ff3699a8 100755 --- a/custom/bin/buildsuper_x64-mac.sh +++ b/custom/bin/buildsuper_x64-mac.sh @@ -24,7 +24,7 @@ clang++ -I"$CODE_HOME" $meta_macros $arch $opts $debug -std=gnu++0x "$SOURCE" -E clang++ -I"$CODE_HOME" $opts $debug -std=gnu++0x "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" "$CODE_HOME/metadata_generator" -R "$CODE_HOME" "$PWD/$preproc_file" -clang++ -I"$CODE_HOME" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC +clang++ -I"$CODE_HOME" $arch $opts $debug -std=c++11 "$SOURCE" -shared -o custom_4coder.so -fPIC rm "$CODE_HOME/metadata_generator" rm $preproc_file diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 2318c698..228d87bc 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -624,8 +624,7 @@ mac_toggle_fullscreen(void){ } - (void)viewDidChangeBackingProperties{ - // TODO(yuval): Screen scale factor calculation - printf("Backing changed!\n"); + // TODO(yuval): If the screen scale factor changed, modify the current face to use the new screen scale factor. mac_resize(mac_vars.window); } @@ -722,7 +721,6 @@ mac_toggle_fullscreen(void){ // NOTE(yuval): Quit the app if requested by the application core MacProfileScope("Perform Kill"){ if (result.perform_kill){ - printf("Terminating 4coder!\n"); [NSApp terminate:nil]; } } @@ -993,7 +991,6 @@ mac_toggle_fullscreen(void){ } - (void)request_display{ - //printf("Display Requested!\n"); CGRect cg_rect = CGRectMake(0, 0, mac_vars.width, mac_vars.height); NSRect rect = NSRectFromCGRect(cg_rect); [self setNeedsDisplayInRect:rect]; @@ -1358,25 +1355,6 @@ main(int arg_count, char **args){ mac_vars.timer_start = system_now_time(); // NOTE(yuval): Start the app's run loop -#if 1 - printf("Running using NSApp run\n"); [NSApp run]; -#else - printf("Running using manual event loop\n"); - - for (;;) { - u64 count = 0; - - NSEvent* event; - do { - event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantFuture] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - - [NSApp sendEvent:event]; - } while (event != nil); - } -#endif } } \ No newline at end of file From 2375a40b29b5b24a1e4c979afb94bc9fc468b40d Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 16 Jan 2020 01:07:49 +0200 Subject: [PATCH 66/67] Ifdefed some debug prints to work only on internal builds. --- platform_mac/mac_4ed.mm | 2 +- platform_mac/mac_4ed_metal.mm | 4 ++++ platform_mac/mac_4ed_opengl.mm | 7 +++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 228d87bc..7e792f4f 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -1283,7 +1283,7 @@ main(int arg_count, char **args){ [mac_vars.window makeKeyAndOrderFront:nil]; // NOTE(yuval): Initialize the renderer - renderer = mac_init_renderer(MacRenderer_OpenGL, mac_vars.window, &target); + renderer = mac_init_renderer(MacRenderer_Metal, mac_vars.window, &target); mac_resize(w, h); diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 0ce3397d..3b03423b 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -15,6 +15,10 @@ struct Mac_Metal{ function mac_render_sig(mac_metal__render){ +#if defined(FRED_INTERNAL) + printf("Redering using Metal!\n"); +#endif + Mac_Metal *metal = (Mac_Metal*)renderer; [metal->view draw]; } diff --git a/platform_mac/mac_4ed_opengl.mm b/platform_mac/mac_4ed_opengl.mm index 7efb31c0..dcfe3715 100644 --- a/platform_mac/mac_4ed_opengl.mm +++ b/platform_mac/mac_4ed_opengl.mm @@ -59,7 +59,6 @@ struct Mac_OpenGL{ // NOTE(yuval): Setup vsync GLint swap_int = 1; - printf("Using vsync: %d\n", swap_int); [[self openGLContext] setValues:&swap_int forParameter:NSOpenGLCPSwapInterval]; } @@ -125,7 +124,9 @@ struct Mac_OpenGL{ function mac_render_sig(mac_gl__render){ - printf("Rendering using OpenGL!\n"); +#if defined(FRED_INTERNAL) + printf("Redering using OpenGL!\n"); +#endif Mac_OpenGL *gl = (Mac_OpenGL*)renderer; [gl->view render:target]; @@ -177,8 +178,6 @@ mac_gl__init(NSWindow *window, Render_Target *target){ // TODO(yuval): This function should be exported to a DLL function mac_load_renderer_sig(mac_load_opengl_renderer){ - printf("Loding The OpenGL Renderer!\n"); - Mac_Renderer *renderer = (Mac_Renderer*)mac_gl__init(window, target); return(renderer); } From b6f6663727b88bc5b8fbbdf7720dd56e9cc974cc Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 17 Jan 2020 00:49:08 +0200 Subject: [PATCH 67/67] Added a key mapping for the equal key. --- custom/generated/4coder_event_codes.h | 400 +++++++++++++------------- platform_mac/mac_4ed.mm | 1 + 2 files changed, 201 insertions(+), 200 deletions(-) diff --git a/custom/generated/4coder_event_codes.h b/custom/generated/4coder_event_codes.h index 8358d6a2..633caf07 100644 --- a/custom/generated/4coder_event_codes.h +++ b/custom/generated/4coder_event_codes.h @@ -1,212 +1,212 @@ enum{ -KeyCode_A = 1, -KeyCode_B = 2, -KeyCode_C = 3, -KeyCode_D = 4, -KeyCode_E = 5, -KeyCode_F = 6, -KeyCode_G = 7, -KeyCode_H = 8, -KeyCode_I = 9, -KeyCode_J = 10, -KeyCode_K = 11, -KeyCode_L = 12, -KeyCode_M = 13, -KeyCode_N = 14, -KeyCode_O = 15, -KeyCode_P = 16, -KeyCode_Q = 17, -KeyCode_R = 18, -KeyCode_S = 19, -KeyCode_T = 20, -KeyCode_U = 21, -KeyCode_V = 22, -KeyCode_W = 23, -KeyCode_X = 24, -KeyCode_Y = 25, -KeyCode_Z = 26, -KeyCode_0 = 27, -KeyCode_1 = 28, -KeyCode_2 = 29, -KeyCode_3 = 30, -KeyCode_4 = 31, -KeyCode_5 = 32, -KeyCode_6 = 33, -KeyCode_7 = 34, -KeyCode_8 = 35, -KeyCode_9 = 36, -KeyCode_Space = 37, -KeyCode_Tick = 38, -KeyCode_Minus = 39, -KeyCode_Equal = 40, -KeyCode_LeftBracket = 41, -KeyCode_RightBracket = 42, -KeyCode_Semicolon = 43, -KeyCode_Quote = 44, -KeyCode_Comma = 45, -KeyCode_Period = 46, -KeyCode_ForwardSlash = 47, -KeyCode_BackwardSlash = 48, -KeyCode_Tab = 49, -KeyCode_Escape = 50, -KeyCode_Pause = 51, -KeyCode_Up = 52, -KeyCode_Down = 53, -KeyCode_Left = 54, -KeyCode_Right = 55, -KeyCode_Backspace = 56, -KeyCode_Return = 57, -KeyCode_Delete = 58, -KeyCode_Insert = 59, -KeyCode_Home = 60, -KeyCode_End = 61, -KeyCode_PageUp = 62, -KeyCode_PageDown = 63, -KeyCode_CapsLock = 64, -KeyCode_NumLock = 65, -KeyCode_ScrollLock = 66, -KeyCode_Menu = 67, -KeyCode_Shift = 68, -KeyCode_Control = 69, -KeyCode_Alt = 70, -KeyCode_Command = 71, -KeyCode_F1 = 72, -KeyCode_F2 = 73, -KeyCode_F3 = 74, -KeyCode_F4 = 75, -KeyCode_F5 = 76, -KeyCode_F6 = 77, -KeyCode_F7 = 78, -KeyCode_F8 = 79, -KeyCode_F9 = 80, -KeyCode_F10 = 81, -KeyCode_F11 = 82, -KeyCode_F12 = 83, -KeyCode_F13 = 84, -KeyCode_F14 = 85, -KeyCode_F15 = 86, -KeyCode_F16 = 87, -KeyCode_COUNT = 88, + KeyCode_A = 1, + KeyCode_B = 2, + KeyCode_C = 3, + KeyCode_D = 4, + KeyCode_E = 5, + KeyCode_F = 6, + KeyCode_G = 7, + KeyCode_H = 8, + KeyCode_I = 9, + KeyCode_J = 10, + KeyCode_K = 11, + KeyCode_L = 12, + KeyCode_M = 13, + KeyCode_N = 14, + KeyCode_O = 15, + KeyCode_P = 16, + KeyCode_Q = 17, + KeyCode_R = 18, + KeyCode_S = 19, + KeyCode_T = 20, + KeyCode_U = 21, + KeyCode_V = 22, + KeyCode_W = 23, + KeyCode_X = 24, + KeyCode_Y = 25, + KeyCode_Z = 26, + KeyCode_0 = 27, + KeyCode_1 = 28, + KeyCode_2 = 29, + KeyCode_3 = 30, + KeyCode_4 = 31, + KeyCode_5 = 32, + KeyCode_6 = 33, + KeyCode_7 = 34, + KeyCode_8 = 35, + KeyCode_9 = 36, + KeyCode_Space = 37, + KeyCode_Tick = 38, + KeyCode_Minus = 39, + KeyCode_Equal = 40, + KeyCode_LeftBracket = 41, + KeyCode_RightBracket = 42, + KeyCode_Semicolon = 43, + KeyCode_Quote = 44, + KeyCode_Comma = 45, + KeyCode_Period = 46, + KeyCode_ForwardSlash = 47, + KeyCode_BackwardSlash = 48, + KeyCode_Tab = 49, + KeyCode_Escape = 50, + KeyCode_Pause = 51, + KeyCode_Up = 52, + KeyCode_Down = 53, + KeyCode_Left = 54, + KeyCode_Right = 55, + KeyCode_Backspace = 56, + KeyCode_Return = 57, + KeyCode_Delete = 58, + KeyCode_Insert = 59, + KeyCode_Home = 60, + KeyCode_End = 61, + KeyCode_PageUp = 62, + KeyCode_PageDown = 63, + KeyCode_CapsLock = 64, + KeyCode_NumLock = 65, + KeyCode_ScrollLock = 66, + KeyCode_Menu = 67, + KeyCode_Shift = 68, + KeyCode_Control = 69, + KeyCode_Alt = 70, + KeyCode_Command = 71, + KeyCode_F1 = 72, + KeyCode_F2 = 73, + KeyCode_F3 = 74, + KeyCode_F4 = 75, + KeyCode_F5 = 76, + KeyCode_F6 = 77, + KeyCode_F7 = 78, + KeyCode_F8 = 79, + KeyCode_F9 = 80, + KeyCode_F10 = 81, + KeyCode_F11 = 82, + KeyCode_F12 = 83, + KeyCode_F13 = 84, + KeyCode_F14 = 85, + KeyCode_F15 = 86, + KeyCode_F16 = 87, + KeyCode_COUNT = 88, }; global char* key_code_name[KeyCode_COUNT] = { -"None", -"A", -"B", -"C", -"D", -"E", -"F", -"G", -"H", -"I", -"J", -"K", -"L", -"M", -"N", -"O", -"P", -"Q", -"R", -"S", -"T", -"U", -"V", -"W", -"X", -"Y", -"Z", -"0", -"1", -"2", -"3", -"4", -"5", -"6", -"7", -"8", -"9", -"Space", -"Tick", -"Minus", -"Equal", -"LeftBracket", -"RightBracket", -"Semicolon", -"Quote", -"Comma", -"Period", -"ForwardSlash", -"BackwardSlash", -"Tab", -"Escape", -"Pause", -"Up", -"Down", -"Left", -"Right", -"Backspace", -"Return", -"Delete", -"Insert", -"Home", -"End", -"PageUp", -"PageDown", -"CapsLock", -"NumLock", -"ScrollLock", -"Menu", -"Shift", -"Control", -"Alt", -"Command", -"F1", -"F2", -"F3", -"F4", -"F5", -"F6", -"F7", -"F8", -"F9", -"F10", -"F11", -"F12", -"F13", -"F14", -"F15", -"F16", + "None", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "Space", + "Tick", + "Minus", + "Equal", + "LeftBracket", + "RightBracket", + "Semicolon", + "Quote", + "Comma", + "Period", + "ForwardSlash", + "BackwardSlash", + "Tab", + "Escape", + "Pause", + "Up", + "Down", + "Left", + "Right", + "Backspace", + "Return", + "Delete", + "Insert", + "Home", + "End", + "PageUp", + "PageDown", + "CapsLock", + "NumLock", + "ScrollLock", + "Menu", + "Shift", + "Control", + "Alt", + "Command", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16", }; enum{ -MouseCode_Left = 1, -MouseCode_Middle = 2, -MouseCode_Right = 3, -MouseCode_COUNT = 4, + MouseCode_Left = 1, + MouseCode_Middle = 2, + MouseCode_Right = 3, + MouseCode_COUNT = 4, }; global char* mouse_code_name[MouseCode_COUNT] = { -"None", -"Left", -"Middle", -"Right", + "None", + "Left", + "Middle", + "Right", }; enum{ -CoreCode_Startup = 1, -CoreCode_Animate = 2, -CoreCode_ClickActivateView = 3, -CoreCode_ClickDeactivateView = 4, -CoreCode_TryExit = 5, -CoreCode_FileExternallyModified = 6, -CoreCode_NewClipboardContents = 7, -CoreCode_COUNT = 8, + CoreCode_Startup = 1, + CoreCode_Animate = 2, + CoreCode_ClickActivateView = 3, + CoreCode_ClickDeactivateView = 4, + CoreCode_TryExit = 5, + CoreCode_FileExternallyModified = 6, + CoreCode_NewClipboardContents = 7, + CoreCode_COUNT = 8, }; global char* core_code_name[CoreCode_COUNT] = { -"None", -"Startup", -"Animate", -"ClickActivateView", -"ClickDeactivateView", -"TryExit", -"FileExternallyModified", -"NewClipboardContents", + "None", + "Startup", + "Animate", + "ClickActivateView", + "ClickDeactivateView", + "TryExit", + "FileExternallyModified", + "NewClipboardContents", }; diff --git a/platform_mac/mac_4ed.mm b/platform_mac/mac_4ed.mm index 7e792f4f..e4ca5ec1 100644 --- a/platform_mac/mac_4ed.mm +++ b/platform_mac/mac_4ed.mm @@ -387,6 +387,7 @@ mac_keycode_init(void){ keycode_lookup_table[kVK_Space] = KeyCode_Space; keycode_lookup_table[kVK_ANSI_Grave] = KeyCode_Tick; keycode_lookup_table[kVK_ANSI_Minus] = KeyCode_Minus; + keycode_lookup_table[kVK_ANSI_Equal] = KeyCode_Equal; keycode_lookup_table[kVK_ANSI_LeftBracket] = KeyCode_LeftBracket; keycode_lookup_table[kVK_ANSI_RightBracket] = KeyCode_RightBracket; keycode_lookup_table[kVK_ANSI_Semicolon] = KeyCode_Semicolon;