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/platform_linux/linux_4ed.cpp b/platform_linux/linux_4ed.cpp index 81ec6464..07e88b77 100644 --- a/platform_linux/linux_4ed.cpp +++ b/platform_linux/linux_4ed.cpp @@ -1,2286 +1,85 @@ /* - * Mr. 4th Dimention - Allen Webster - * (Mostly by insofaras) + * chr - Andrew Chronister * - * 14.11.2015 + * 12.19.2019 * - * Linux layer for 4coder + * Updated linux layer for 4coder * */ // TOP -#define IS_PLAT_LAYER +#define FPS 60 +#define frame_useconds (1000000 / FPS) -#include #include "4coder_base_types.h" -#include "4coder_API/4coder_version.h" +#include "4coder_version.h" +#include "4coder_events.h" -#include "4coder_lib/4coder_utf8.h" +#include "4coder_table.h" +#include "4coder_types.h" +#include "4coder_default_colors.h" -#if defined(FRED_SUPER) -#include "4coder_keycode_extension.h" -# include "4coder_API/4coder_style.h" +#include "4coder_system_types.h" +#define STATIC_LINK_API +#include "generated/system_api.h" -# include "4coder_lib/4coder_arena.h" -# include "4coder_lib/4coder_arena.cpp" -# define FSTRING_IMPLEMENTATION -# include "4coder_lib/4coder_string.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 "4coder_API/4coder_types.h" - -#else -# include "4coder_default_bindings.cpp" -#endif - -#include "4ed_math.h" - -#include "4ed_font.h" -#include "4ed_system.h" +#include "4ed_font_set.h" #include "4ed_render_target.h" -#include "4ed_render_format.h" +#include "4ed_search_list.h" #include "4ed.h" -#include "4ed_linked_node_macros.h" -#include "4ed_file_track.h" -#include "4ed_system_shared.h" +#include "generated/system_api.cpp" +#include "generated/graphics_api.cpp" +#include "generated/font_api.cpp" -#include "unix_4ed_headers.h" -#include -#include +#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 -#include -#include +#include "4ed_search_list.cpp" -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "4ed_shared_thread_constants.h" -#include "unix_threading_wrapper.h" -#include "linux_semaphore_wrapper.h" - -// -// Linux macros -// - -#define FPS 60L -#define frame_useconds (1000000UL / FPS) - -#define LINUX_FN_DEBUG(fmt, ...) do { \ - /*LOGF("%s: " fmt "\n", __func__, ##__VA_ARGS__);*/ \ -} while (0) - -// TODO(allen): Make an intrinsics header that uses the cracked OS to define a single set of intrinsic names. -#define InterlockedCompareExchange(dest, ex, comp) \ -__sync_val_compare_and_swap((dest), (comp), (ex)) - -// -// Linux structs / enums -// - -enum{ - LINUX_4ED_EVENT_X11 = (UINT64_C(1) << 32), - LINUX_4ED_EVENT_X11_INTERNAL = (UINT64_C(2) << 32), - LINUX_4ED_EVENT_STEP = (UINT64_C(3) << 32), - LINUX_4ED_EVENT_STEP_TIMER = (UINT64_C(4) << 32), - LINUX_4ED_EVENT_CLI = (UINT64_C(5) << 32), -}; - -// -// Linux forward declarations -// - -internal void LinuxStringDup(String*, void*, size_t); - -//////////////////////////////// - -#define SLASH '/' -#define DLL "so" - -global System_Functions sysfunc; -#include "4ed_shared_library_constants.h" -#include "unix_library_wrapper.h" - -#include "4ed_standard_libraries.cpp" -#include "4ed_coroutine.cpp" -#include "4ed_font.cpp" - -//////////////////////////////// - -struct Linux_Vars{ - Display *XDisplay; - Window XWindow; - - XIM input_method; - XIMStyle input_style; - XIC input_context; - - Application_Step_Input input; - b8 modifiers[MDFR_INDEX_COUNT]; - - String clipboard_contents; - String clipboard_outgoing; - b32 new_clipboard; - - Atom atom_TARGETS; - Atom atom_CLIPBOARD; - Atom atom_UTF8_STRING; - Atom atom__NET_WM_STATE; - Atom atom__NET_WM_STATE_MAXIMIZED_HORZ; - Atom atom__NET_WM_STATE_MAXIMIZED_VERT; - Atom atom__NET_WM_STATE_FULLSCREEN; - Atom atom__NET_WM_PING; - Atom atom__NET_WM_WINDOW_TYPE; - Atom atom__NET_WM_WINDOW_TYPE_NORMAL; - Atom atom__NET_WM_PID; - Atom atom_WM_DELETE_WINDOW; - - b32 has_xfixes; - int xfixes_selection_event; - - int epoll; - - int step_timer_fd; - int step_event_fd; - int x11_fd; - int inotify_fd; - - u64 last_step; - - b32 full_screen; - b32 do_toggle; - b32 keep_running; - - Application_Mouse_Cursor cursor; - b32 hide_cursor; - Cursor hidden_cursor; - - sem_t thread_semaphore; - - i32 dpi_x, dpi_y; - - b32 vsync; - - File_Track_System track; - void *track_table; - u32 track_table_size; - u32 track_node_size; -}; - -//////////////////////////////// - -global Linux_Vars linuxvars; -global Render_Target target; -global Application_Memory memory_vars; -global Plat_Settings plat_settings; - -global Libraries libraries; -global App_Functions app; -global Custom_API custom_api; - -global Coroutine_System_Auto_Alloc coroutines; - -//////////////////////////////// - -#include "linux_icon.h" -internal void -linux_set_icon(Display* d, Window w){ - Atom WM_ICON = XInternAtom(d, "_NET_WM_ICON", False); - XChangeProperty(d, w, WM_ICON, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)linux_icon, sizeof(linux_icon) / sizeof(long)); -} - -#include "linux_error_box.cpp" - -//////////////////////////////// - -internal sem_t* -handle_sem(Plat_Handle h){ - return(*(sem_t**)&h); -} - -internal Plat_Handle -handle_sem(sem_t *sem){ - return(*(Plat_Handle*)&sem); -} - -//////////////////////////////// +#include internal -Sys_Get_4ed_Path_Sig(system_get_4ed_path){ - ssize_t size = readlink("/proc/self/exe", out, capacity - 1); - if (size != -1 && size < capacity - 1){ - String str = make_string(out, size); - remove_last_folder(&str); - terminate_with_null(&str); - size = str.size; - } - else{ - size = 0; - } - return(size); -} - -#include "unix_4ed_functions.cpp" - -internal -Sys_Now_Time_Sig(system_now_time){ - struct timespec spec; - clock_gettime(CLOCK_REALTIME, &spec); - u64 result = (spec.tv_sec*UINT64_C(1000000)) + (spec.tv_nsec/UINT64_C(1000)); - return(result); -} - -//////////////////////////////// - -internal void -system_schedule_step(){ - u64 now = system_now_time(); - u64 diff = (now - linuxvars.last_step); - if (diff > (u64)frame_useconds){ - u64 ev = 1; - ssize_t size = write(linuxvars.step_event_fd, &ev, sizeof(ev)); - AllowLocal(size); - } - else{ - struct itimerspec its = {}; - timerfd_gettime(linuxvars.step_timer_fd, &its); - if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0){ - its.it_value.tv_nsec = (frame_useconds - diff) * 1000UL; - timerfd_settime(linuxvars.step_timer_fd, 0, &its, NULL); - } - } -} - -//////////////////////////////// - -#include "4ed_work_queues.cpp" - -//////////////////////////////// - -internal void -linux_set_wm_state(Display* d, Window w, Atom one, Atom two, int mode){ - //NOTE(inso): this will only work after it is mapped - - enum { STATE_REMOVE, STATE_ADD, STATE_TOGGLE }; - - XEvent e = {}; - - e.xany.type = ClientMessage; - e.xclient.message_type = linuxvars.atom__NET_WM_STATE; - e.xclient.format = 32; - e.xclient.window = w; - e.xclient.data.l[0] = mode; - e.xclient.data.l[1] = one; - e.xclient.data.l[2] = two; - e.xclient.data.l[3] = 1L; - - XSendEvent(d, RootWindow(d, 0), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); -} - -internal void -linux_maximize_window(b32 maximize){ - linux_set_wm_state(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_STATE_MAXIMIZED_HORZ, linuxvars.atom__NET_WM_STATE_MAXIMIZED_VERT, maximize?1:0); -} - -internal void -linux_toggle_fullscreen(){ - linuxvars.full_screen = !linuxvars.full_screen; - linux_set_wm_state(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_STATE_FULLSCREEN, 0, linuxvars.full_screen?1:0); -} - -internal -Sys_Show_Mouse_Cursor_Sig(system_show_mouse_cursor){ - linuxvars.hide_cursor = !show; - XDefineCursor(linuxvars.XDisplay, linuxvars.XWindow, show?None:linuxvars.hidden_cursor); -} - -internal -Sys_Set_Fullscreen_Sig(system_set_fullscreen){ - b32 success = true; - linuxvars.do_toggle = (linuxvars.full_screen != full_screen); - return(success); -} - -internal -Sys_Is_Fullscreen_Sig(system_is_fullscreen){ - b32 result = (linuxvars.full_screen != linuxvars.do_toggle); - return(result); -} - -#include "4ed_coroutine_functions.cpp" - -#include "4ed_system_shared.cpp" - -// -// File Change Listener -// - -internal b32 -handle_track_out_of_memory(i32 val){ - b32 result = false; - - switch (val){ - case FileTrack_OutOfTableMemory: +system_get_path_sig(){ + // Arena* arena, System_Path_Code path_code + String_Const_u8 result = {}; + switch (path_code){ + case SystemPath_CurrentDirectory: { - u32 new_table_size = linuxvars.track_table_size*2; - void *new_table = system_memory_allocate(new_table_size); - move_track_system(&linuxvars.track, &shared_vars.scratch, new_table, new_table_size); - system_memory_free(linuxvars.track_table, linuxvars.track_table_size); - linuxvars.track_table_size = new_table_size; - linuxvars.track_table = new_table; + // glibc extension: getcwd allocates its own memory if passed NULL + char *working_dir = getcwd(NULL, 0); + u64 working_dir_len = cstring_length(working_dir); + u8 *out = push_array(arena, u8, working_dir_len); + block_copy(out, working_dir, working_dir_len); + free(working_dir); + result = SCu8(out, working_dir_len); }break; - - case FileTrack_OutOfListenerMemory: + + case SystemPath_Binary: { - linuxvars.track_node_size *= 2; - void *node_expansion = system_memory_allocate(linuxvars.track_node_size); - expand_track_system_listeners(&linuxvars.track, &shared_vars.scratch, node_expansion, linuxvars.track_node_size); + // linux-specific: binary path symlinked at /proc/self/exe + ssize_t binary_path_len = readlink("/proc/self/exe", NULL, 0); + u8* out = push_array(arena, u8, binary_path_len); + readlink("/proc/self/exe", (char*)out, binary_path_len); + String_u8 out_str = Su8(out, binary_path_len); + out_str.string = string_remove_last_folder(out_str.string); + string_null_terminate(&out_str); + result = out_str.string; }break; - - default: result = true; break; - } - - return(result); -} - -internal -Sys_Add_Listener_Sig(system_add_listener){ - b32 result = false; - for (;;){ - i32 track_result = add_listener(&linuxvars.track, &shared_vars.scratch, (u8*)filename); - if (handle_track_out_of_memory(track_result)){ - if (track_result == FileTrack_Good){ - result = true; - } - break; - } } return(result); } -internal -Sys_Remove_Listener_Sig(system_remove_listener){ - b32 result = false; - i32 track_result = remove_listener(&linuxvars.track, &shared_vars.scratch, (u8*)filename); - if (track_result == FileTrack_Good){ - result = true; - } - return(result); +int main(int argc, char **argv){ } - -internal -Sys_Get_File_Change_Sig(system_get_file_change){ - b32 result = false; - - i32 size = 0; - i32 get_result = get_change_event(&linuxvars.track, &shared_vars.scratch, (u8*)buffer, max, &size); - - *required_size = size; - *mem_too_small = false; - if (get_result == FileTrack_Good){ - result = true; - } - else if (get_result == FileTrack_MemoryTooSmall){ - *mem_too_small = true; - result = true; - } - - return(result); -} - -// -// Clipboard -// - -internal -Sys_Post_Clipboard_Sig(system_post_clipboard){ - LinuxStringDup(&linuxvars.clipboard_outgoing, str.str, str.size); - XSetSelectionOwner(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); -} - -// -// CLI -// - -internal -Sys_CLI_Call_Sig(system_cli_call, path, script_name, cli_out){ - LINUX_FN_DEBUG("%s %s", path, script_name); - - int pipe_fds[2]; - if (pipe(pipe_fds) == -1){ - perror("system_cli_call: pipe"); - return 0; - } - - pid_t child_pid = fork(); - if (child_pid == -1){ - perror("system_cli_call: fork"); - return 0; - } - - enum { PIPE_FD_READ, PIPE_FD_WRITE }; - - // child - if (child_pid == 0){ - close(pipe_fds[PIPE_FD_READ]); - dup2(pipe_fds[PIPE_FD_WRITE], STDOUT_FILENO); - dup2(pipe_fds[PIPE_FD_WRITE], STDERR_FILENO); - - if (chdir(path) == -1){ - perror("system_cli_call: chdir"); - exit(1); - } - - char* argv[] = { "sh", "-c", script_name, NULL }; - - if (execv("/bin/sh", argv) == -1){ - perror("system_cli_call: execv"); - } - exit(1); - } - else{ - close(pipe_fds[PIPE_FD_WRITE]); - - *(pid_t*)&cli_out->proc = child_pid; - *(int*)&cli_out->out_read = pipe_fds[PIPE_FD_READ]; - *(int*)&cli_out->out_write = pipe_fds[PIPE_FD_WRITE]; - - struct epoll_event e = {}; - e.events = EPOLLIN | EPOLLET; - e.data.u64 = LINUX_4ED_EVENT_CLI; - epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, pipe_fds[PIPE_FD_READ], &e); - } - - return(true); -} - -internal -Sys_CLI_Begin_Update_Sig(system_cli_begin_update){ - // NOTE(inso): I don't think anything needs to be done here. -} - -internal -Sys_CLI_Update_Step_Sig(system_cli_update_step){ - int pipe_read_fd = *(int*)&cli->out_read; - - fd_set fds; - FD_ZERO(&fds); - FD_SET(pipe_read_fd, &fds); - - struct timeval tv = {}; - - size_t space_left = max; - char* ptr = dest; - - while (space_left > 0 && select(pipe_read_fd + 1, &fds, NULL, NULL, &tv) == 1){ - ssize_t num = read(pipe_read_fd, ptr, space_left); - if (num == -1){ - perror("system_cli_update_step: read"); - } else if (num == 0){ - // NOTE(inso): EOF - break; - } else { - ptr += num; - space_left -= num; - } - } - - *amount = (ptr - dest); - return((ptr - dest) > 0); -} - -internal -Sys_CLI_End_Update_Sig(system_cli_end_update){ - pid_t pid = *(pid_t*)&cli->proc; - b32 close_me = false; - - int status; - if (pid && waitpid(pid, &status, WNOHANG) > 0){ - cli->exit = WEXITSTATUS(status); - - close_me = true; - close(*(int*)&cli->out_read); - close(*(int*)&cli->out_write); - - struct epoll_event e = {}; - epoll_ctl(linuxvars.epoll, EPOLL_CTL_DEL, *(int*)&cli->out_read, &e); - } - - return(close_me); -} - -#include "4ed_font_provider_freetype.h" -global u32 system_font_method = SystemFontMethod_FilePath; -#include "4ed_font_provider_freetype.cpp" - -#undef internal -#include -#define internal static - -global FcConfig *fc_config = 0; - -internal -Sys_Font_Path(name, parameters){ - if (fc_config == 0){ - fc_config = FcInitLoadConfigAndFonts(); - } - - Font_Path path = {}; - - FcPattern *pattern_regular = FcPatternBuild( - 0, - FC_POSTSCRIPT_NAME, FcTypeString, name, - FC_SIZE, FcTypeDouble, (double)parameters->pt_size, - FC_FONTFORMAT, FcTypeString, "TrueType", - FC_STYLE, FcTypeString, (FcChar8*)"Regular", - (void*)0); - - FcPattern *pattern_styled = 0; - if (parameters->italics || parameters->bold){ - if (parameters->italics && !parameters->bold){ - pattern_styled = FcPatternBuild( - 0, - FC_POSTSCRIPT_NAME, FcTypeString, name, - FC_SIZE, FcTypeDouble, (double)parameters->pt_size, - FC_FONTFORMAT, FcTypeString, "TrueType", - FC_STYLE, FcTypeString, (FcChar8*)"Italic", - (void*)0); - } - else if (!parameters->italics && parameters->bold){ - pattern_styled = FcPatternBuild( - 0, - FC_POSTSCRIPT_NAME, FcTypeString, name, - FC_SIZE, FcTypeDouble, (double)parameters->pt_size, - FC_FONTFORMAT, FcTypeString, "TrueType", - FC_STYLE, FcTypeString, (FcChar8*)"Bold", - (void*)0); - } - else{ - pattern_styled = FcPatternBuild( - 0, - FC_POSTSCRIPT_NAME, FcTypeString, name, - FC_SIZE, FcTypeDouble, (double)parameters->pt_size, - FC_FONTFORMAT, FcTypeString, "TrueType", - FC_STYLE, FcTypeString, (FcChar8*)"Bold Italic", - (void*)0); - } - } - - Partition *part = &shared_vars.font_scratch; - path.temp = begin_temp_memory(part); - - b32 got_font = false; - if (pattern_styled != 0){ - if (FcConfigSubstitute(fc_config, pattern_styled, FcMatchPattern)){ - FcDefaultSubstitute(pattern_styled); - - FcResult res; - FcPattern *font = FcFontMatch(fc_config, pattern_styled, &res); - FcChar8 *filename = 0; - - if (font != 0){ - FcPatternGetString(font, FC_FILE, 0, &filename); - if (filename != 0){ - i32 len = str_size((char*)filename); - char *buffer = push_array(part, char, len + 1); - if (buffer == 0){ - sysshared_partition_grow(part, l_round_up_i32(len + 1, KB(4))); - buffer = push_array(part, char, len + 1); - } - - if (buffer != 0){ - push_align(part, 8); - memcpy(buffer, filename, len + 1); - path.len = len; - path.name = buffer; - } - - got_font = true; - } - FcPatternDestroy(font); - } - } - - FcPatternDestroy(pattern_styled); - pattern_styled = 0; - } - - if (!got_font){ - if (FcConfigSubstitute(fc_config, pattern_regular, FcMatchPattern)){ - FcDefaultSubstitute(pattern_regular); - - FcResult res; - FcPattern *font = FcFontMatch(fc_config, pattern_regular, &res); - FcChar8 *filename = 0; - - if (font != 0){ - FcPatternGetString(font, FC_FILE, 0, &filename); - if (filename != 0){ - i32 len = str_size((char*)filename); - char *buffer = push_array(part, char, len + 1); - if (buffer == 0){ - sysshared_partition_grow(part, l_round_up_i32(len + 1, KB(4))); - buffer = push_array(part, char, len + 1); - } - - if (buffer != 0){ - push_align(part, 8); - memcpy(buffer, filename, len + 1); - path.len = len; - path.name = buffer; - } - - got_font = true; - } - FcPatternDestroy(font); - } - } - - FcPatternDestroy(pattern_styled); - } - - FcPatternDestroy(pattern_regular); - - return(path); -} - -Sys_Font_Data_Not_Used; - -internal char* -linux_get_loadable_fonts(Partition *part, Font_Setup_List *list){ - if (fc_config == 0){ - fc_config = FcInitLoadConfigAndFonts(); - } - - FcPattern* pat = FcPatternBuild( - 0, - FC_STYLE, FcTypeString, (FcChar8*)"Regular", - (void*)0); - FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_FILE, (char*)0); - FcFontSet* fs = FcFontList(fc_config, pat, os); - if (fs != 0){ - //LOGF("Total matching fonts: %d\n", fs->nfont); - for (int i=0; fs && i < fs->nfont; ++i) { - FcPattern* font = fs->fonts[i]; - FcChar8 *file = 0; - FcChar8 *style = 0; - FcChar8 *family = 0; - - if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch && - FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch) - { - Temp_Memory reset = begin_temp_memory(part); - Font_Setup *setup = push_array(part, Font_Setup, 1); - if (setup != 0){ - memset(setup, 0, sizeof(*setup)); - - i32 len = str_size((char*)file); - if (len < sizeof(setup->stub.name)){ - i32 name_len = str_size((char*)family); - if (name_len < sizeof(setup->name)){ - setup->stub.load_from_path = true; - memcpy(setup->stub.name, file, len + 1); - setup->stub.len = len; - setup->has_display_name = true; - setup->len = name_len; - memcpy(setup->name, family, name_len + 1); - sll_push(list->first, list->last, setup); - } - else{ - end_temp_memory(reset); - } - } - else{ - end_temp_memory(reset); - } - } - } - } - FcFontSetDestroy(fs); - } -} - -#include -#include "opengl/4ed_opengl_render.cpp" - -// -// End of system funcs -// - -// -// Renderer -// - -internal void -LinuxResizeTarget(i32 width, i32 height){ - if (width > 0 && height > 0){ - target.width = width; - target.height = height; - } -} - -// -// OpenGL init -// - -// NOTE(allen): Thanks to Casey for providing the linux OpenGL launcher. -static bool ctxErrorOccurred = false; - -internal int -ctxErrorHandler( Display *dpy, XErrorEvent *ev ){ - ctxErrorOccurred = true; - return 0; -} - -typedef GLXContext (glXCreateContextAttribsARB_Function)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -typedef void (glXSwapIntervalEXT_Function) (Display *dpy, GLXDrawable drawable, int interval); -typedef int (glXSwapIntervalMESA_Function) (unsigned int interval); -typedef int (glXGetSwapIntervalMESA_Function) (void); -typedef int (glXSwapIntervalSGI_Function) (int interval); - -internal GLXContext -InitializeOpenGLContext(Display *XDisplay, Window XWindow, GLXFBConfig *best_config){ - - const char *glxExts = glXQueryExtensionsString(XDisplay, DefaultScreen(XDisplay)); - - glXCreateContextAttribsARB_Function *glXCreateContextAttribsARB = 0; - glXSwapIntervalEXT_Function *glXSwapIntervalEXT = 0; - glXSwapIntervalMESA_Function *glXSwapIntervalMESA = 0; - glXGetSwapIntervalMESA_Function *glXGetSwapIntervalMESA = 0; - glXSwapIntervalSGI_Function *glXSwapIntervalSGI = 0; - -#define GLXLOAD(f) f = (f##_Function*) glXGetProcAddressARB((const GLubyte*) #f); - GLXLOAD(glXCreateContextAttribsARB); - - GLXContext ctx = 0; - ctxErrorOccurred = false; - int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler); - - if (glXCreateContextAttribsARB == 0){ - //LOG("glXCreateContextAttribsARB() not found, using old-style GLX context\n" ); - ctx = glXCreateNewContext( XDisplay, *best_config, GLX_RGBA_TYPE, 0, True ); - } - else{ - int context_attribs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 2, - GLX_CONTEXT_MINOR_VERSION_ARB, 1, - GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, -#if defined(FRED_INTERNAL) - GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB, -#endif - None - }; - - //LOG("Creating GL 2.1 context... "); - ctx = glXCreateContextAttribsARB(XDisplay, *best_config, 0, True, context_attribs); - - XSync( XDisplay, False ); - if (!ctxErrorOccurred && ctx){ - //LOG("Created GL 2.1 context.\n"); - } - else{ - //LOG("Could not create a context.\n"); - exit(1); - } - } - - XSync(XDisplay, False); - XSetErrorHandler(oldHandler); - - if (ctxErrorOccurred || !ctx){ - //LOG("Failed to create an OpenGL context\n"); - exit(1); - } - - b32 Direct; - if (!glXIsDirect(XDisplay, ctx)){ - //LOG("Indirect GLX rendering context obtained\n"); - Direct = false; - } - else{ - //LOG("Direct GLX rendering context obtained\n"); - Direct = true; - } - - //LOG("Making context current\n"); - glXMakeCurrent( XDisplay, XWindow, ctx ); - - //TODO(inso): glGetStringi is required in core profile if the GL version is >= 3.0 - //char *Extensions = (char *)glGetString(GL_EXTENSIONS); - - //NOTE(inso): enable vsync if available. this should probably be optional - if (Direct && strstr(glxExts, "GLX_EXT_swap_control ")){ - GLXLOAD(glXSwapIntervalEXT); - - if (glXSwapIntervalEXT != 0){ - glXSwapIntervalEXT(XDisplay, XWindow, 1); - - unsigned int swap_val = 0; - glXQueryDrawable(XDisplay, XWindow, GLX_SWAP_INTERVAL_EXT, &swap_val); - - linuxvars.vsync = (swap_val == true); - //LOGF("VSync enabled? %s.\n", linuxvars.vsync ? "Yes" : "No"); - } - - } - else if (Direct && strstr(glxExts, "GLX_MESA_swap_control ")){ - - GLXLOAD(glXSwapIntervalMESA); - GLXLOAD(glXGetSwapIntervalMESA); - - if (glXSwapIntervalMESA != 0){ - glXSwapIntervalMESA(1); - - if (glXGetSwapIntervalMESA != 0){ - linuxvars.vsync = glXGetSwapIntervalMESA(); - //LOGF("VSync enabled? %s (MESA)\n", linuxvars.vsync ? "Yes" : "No"); - } - else{ - // NOTE(inso): assume it worked? - linuxvars.vsync = true; - //LOG("VSync enabled? possibly (MESA)\n"); - } - } - - } - else if (Direct && strstr(glxExts, "GLX_SGI_swap_control ")){ - GLXLOAD(glXSwapIntervalSGI); - - if (glXSwapIntervalSGI){ - glXSwapIntervalSGI(1); - - // NOTE(inso): The SGI one doesn't seem to have a way to confirm we got it... - linuxvars.vsync = true; - //LOG("VSync enabled? hopefully (SGI)\n"); - } - - } - else{ - //LOG("VSync enabled? nope, no suitable extension\n"); - } - - return(ctx); -} - -internal b32 -GLXCanUseFBConfig(Display *XDisplay) -{ - b32 Result = false; - - int GLXMajor, GLXMinor; - - char *XVendor = ServerVendor(XDisplay); - //LOGF("XWindows vendor: %s\n", XVendor); - if (glXQueryVersion(XDisplay, &GLXMajor, &GLXMinor)) - { - //LOGF("GLX version %d.%d\n", GLXMajor, GLXMinor); - if (((GLXMajor == 1 ) && (GLXMinor >= 3)) || (GLXMajor > 1)) - { - Result = true; - } - } - - return(Result); -} - -struct glx_config_result{ - b32 Found; - GLXFBConfig BestConfig; - XVisualInfo BestInfo; -}; - -internal glx_config_result -ChooseGLXConfig(Display *XDisplay, int XScreenIndex) -{ - glx_config_result Result = {}; - - int DesiredAttributes[] = { - GLX_X_RENDERABLE , True, - GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, - GLX_RENDER_TYPE , GLX_RGBA_BIT, - GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, - GLX_RED_SIZE , 8, - GLX_GREEN_SIZE , 8, - GLX_BLUE_SIZE , 8, - GLX_ALPHA_SIZE , 8, - GLX_DEPTH_SIZE , 24, - GLX_STENCIL_SIZE , 8, - GLX_DOUBLEBUFFER , True, - None - }; - - int ConfigCount = 0; - GLXFBConfig *Configs = glXChooseFBConfig(XDisplay, XScreenIndex, DesiredAttributes, &ConfigCount); - if (Configs != 0 && ConfigCount > 0){ - XVisualInfo* VI = glXGetVisualFromFBConfig(XDisplay, Configs[0]); - if (VI != 0){ - Result.Found = true; - Result.BestConfig = Configs[0]; - Result.BestInfo = *VI; - - int id = 0; - glXGetFBConfigAttrib(XDisplay, Result.BestConfig, GLX_FBCONFIG_ID, &id); - //LOGF("Using FBConfig: %d (0x%x)\n", id, id); - - XFree(VI); - } - - XFree(Configs); - } - - return(Result); -} - -// -// X11 input / events init -// - -struct Init_Input_Result{ - XIM input_method; - XIMStyle best_style; - XIC xic; -}; -static Init_Input_Result null_init_input_result = {}; - -internal Init_Input_Result -LinuxInputInit(Display *dpy, Window XWindow){ - Init_Input_Result result = {}; - XIMStyles *styles = 0; - XIMStyle style; - unsigned long xim_event_mask = 0; - - setlocale(LC_ALL, ""); - XSetLocaleModifiers(""); - b32 locale_supported = XSupportsLocale(); - //LOGF("Supported locale?: %s.\n", locale_supported ? "Yes" : "No"); - if (!locale_supported){ - //LOG("Reverting to 'C' ... "); - setlocale(LC_ALL, "C"); - locale_supported = XSupportsLocale(); - //LOGF("C is supported? %s.\n", locale_supported ? "Yes" : "No"); - } - - result.input_method = XOpenIM(dpy, 0, 0, 0); - if (!result.input_method){ - // NOTE(inso): Try falling back to the internal XIM implementation that - // should in theory always exist. - - XSetLocaleModifiers("@im=none"); - result.input_method = XOpenIM(dpy, 0, 0, 0); - } - - if (result.input_method){ - if (!XGetIMValues(result.input_method, XNQueryInputStyle, &styles, NULL) && styles){ - for (i32 i = 0; i < styles->count_styles; ++i){ - style = styles->supported_styles[i]; - if (style == (XIMPreeditNothing | XIMStatusNothing)){ - result.best_style = style; - break; - } - } - } - - if (result.best_style){ - XFree(styles); - - result.xic = XCreateIC(result.input_method, XNInputStyle, result.best_style, XNClientWindow, XWindow, XNFocusWindow, XWindow, NULL); - - if (XGetICValues(result.xic, XNFilterEvents, &xim_event_mask, NULL)){ - xim_event_mask = 0; - } - } - else{ - result = null_init_input_result; - //LOG("Could not get minimum required input style.\n"); - exit(1); - } - } - else{ - result = null_init_input_result; - //LOG("Could not open X Input Method.\n"); - exit(1); - } - - u32 flags = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask | MappingNotify | ExposureMask | VisibilityChangeMask | xim_event_mask; - - XSelectInput(linuxvars.XDisplay, linuxvars.XWindow, flags); - - return(result); -} - -// -// Keyboard handling funcs -// - -#if 0 -internal void -LinuxKeycodeInit(Display* dpy){ - - // NOTE(inso): This looks a bit dumb, but it's the best way I can think of to do it, since: - // KeySyms are the type representing "virtual" keys, like XK_BackSpace, but they are 32-bit ints. - // KeyCodes are guaranteed to fit in 1 byte (and therefore the keycode_lookup_table) but - // have dynamic numbers assigned by the XServer. - // There is XKeysymToKeycode, but it only returns 1 KeyCode for a KeySym. I have my capslock - // rebound to esc, so there are two KeyCodes for the XK_Escape KeyCode but XKeysymToKeycode only - // gets one of them, hence the need for this crazy lookup which works correctly with rebound keys. - - memset(keycode_lookup_table, 0, sizeof(keycode_lookup_table)); - - struct SymMapping { - KeySym sym; - u16 code; - }; - - SymMapping sym_table[] = { - { XK_BackSpace, key_back }, - { XK_Delete, key_del }, - { XK_Up, key_up }, - { XK_Down, key_down }, - { XK_Left, key_left }, - { XK_Right, key_right }, - { XK_Insert, key_insert }, - { XK_Home, key_home }, - { XK_End, key_end }, - { XK_Page_Up, key_page_up }, - { XK_Page_Down, key_page_down }, - { XK_Escape, key_esc }, - { XK_F1, key_f1 }, - { XK_F2, key_f2 }, - { XK_F3, key_f3 }, - { XK_F4, key_f4 }, - { XK_F5, key_f5 }, - { XK_F6, key_f6 }, - { XK_F7, key_f7 }, - { XK_F8, key_f8 }, - { XK_F9, key_f9 }, - { XK_F10, key_f10 }, - { XK_F11, key_f11 }, - { XK_F12, key_f12 }, - { XK_F13, key_f13 }, - { XK_F14, key_f14 }, - { XK_F15, key_f15 }, - { XK_F16, key_f16 }, - }; - - const int table_size = sizeof(sym_table) / sizeof(struct SymMapping); - - int key_min, key_max, syms_per_code; - XDisplayKeycodes(dpy, &key_min, &key_max); - - int key_count = (key_max - key_min) + 1; - - KeySym* syms = XGetKeyboardMapping(dpy, key_min, key_count, &syms_per_code); - - if (syms == 0){ - return; - } - - int key = key_min; - for(int i = 0; i < key_count * syms_per_code; ++i){ - for(int j = 0; j < table_size; ++j){ - if (sym_table[j].sym == syms[i]){ - keycode_lookup_table[key + (i/syms_per_code)] = sym_table[j].code; - break; - } - } - } - - XFree(syms); -} -#endif - -internal void -LinuxPushKey(Key_Code code, Key_Code chr, Key_Code chr_nocaps, b8 *mods) -{ - i32 *count = &linuxvars.input.keys.count; - Key_Event_Data *data = linuxvars.input.keys.keys; - - if (*count < KEY_INPUT_BUFFER_SIZE){ - data[*count].keycode = code; - data[*count].character = chr; - data[*count].character_no_caps_lock = chr_nocaps; - - memcpy(data[*count].modifiers, mods, sizeof(*mods)*MDFR_INDEX_COUNT); - - ++(*count); - } -} - -// -// Misc utility funcs -// - -internal void -LinuxStringDup(String* str, void* data, size_t size){ - if (str->memory_size < size){ - if (str->str){ - system_memory_free(str->str, str->memory_size); - } - str->memory_size = size; - str->str = (char*)system_memory_allocate(size); - //TODO(inso): handle alloc failure case - } - - str->size = size; - memcpy(str->str, data, size); -} - -// -// X11 utility funcs -// - -internal void -LinuxX11ConnectionWatch(Display* dpy, XPointer cdata, int fd, Bool opening, XPointer* wdata){ - struct epoll_event e = {}; - e.events = EPOLLIN | EPOLLET; - e.data.u64 = LINUX_4ED_EVENT_X11_INTERNAL | fd; - - int op = opening ? EPOLL_CTL_ADD : EPOLL_CTL_DEL; - epoll_ctl(linuxvars.epoll, op, fd, &e); -} - -internal int -LinuxGetXSettingsDPI(Display* dpy, int screen) -{ - struct XSettingHeader { - u8 type; - u8 pad0; - u16 name_len; - char name[0]; - }; - - struct XSettings { - u8 byte_order; - u8 pad[3]; - u32 serial; - u32 num_settings; - }; - - enum { XSettingsTypeInt, XSettingsTypeString, XSettingsTypeColor }; - - int dpi = -1; - unsigned char* prop = NULL; - char sel_buffer[64]; - struct XSettings* xs; - const char* p; - - snprintf(sel_buffer, sizeof(sel_buffer), "_XSETTINGS_S%d", screen); - - Atom XSET_SEL = XInternAtom(dpy, sel_buffer, True); - Atom XSET_SET = XInternAtom(dpy, "_XSETTINGS_SETTINGS", True); - - if (XSET_SEL == None || XSET_SET == None){ - //LOG("XSETTINGS unavailable.\n"); - return(dpi); - } - - Window xset_win = XGetSelectionOwner(dpy, XSET_SEL); - if (xset_win == None){ - // TODO(inso): listen for the ClientMessage about it becoming available? - // there's not much point atm if DPI scaling is only done at startup - goto out; - } - - { - Atom type; - int fmt; - unsigned long pad, num; - - if (XGetWindowProperty(dpy, xset_win, XSET_SET, 0, 1024, False, XSET_SET, &type, &fmt, &num, &pad, &prop) != Success){ - //LOG("XSETTINGS: GetWindowProperty failed.\n"); - goto out; - } - - if (fmt != 8){ - //LOG("XSETTINGS: Wrong format.\n"); - goto out; - } - } - - xs = (struct XSettings*)prop; - p = (char*)(xs + 1); - - if (xs->byte_order != 0){ - //LOG("FIXME: XSETTINGS not host byte order?\n"); - goto out; - } - - for (int i = 0; i < xs->num_settings; ++i){ - struct XSettingHeader* h = (struct XSettingHeader*)p; - - p += sizeof(struct XSettingHeader); - p += h->name_len; - p += ((4 - (h->name_len & 3)) & 3); - p += 4; // serial - - switch (h->type){ - case XSettingsTypeInt: { - if (strncmp(h->name, "Xft/DPI", h->name_len) == 0){ - dpi = *(i32*)p; - if (dpi != -1) dpi /= 1024; - } - p += 4; - } break; - - case XSettingsTypeString: { - u32 len = *(u32*)p; - p += 4; - p += len; - p += ((4 - (len & 3)) & 3); - } break; - - case XSettingsTypeColor: { - p += 8; - } break; - - default: { - //LOG("XSETTINGS: Got invalid type...\n"); - goto out; - } break; - } - } - - out: - if (prop){ - XFree(prop); - } - - return dpi; -} - -// -// X11 window init -// - -internal f32 -size_change(i32 x, i32 y){ - f32 xs = x/96.f; - f32 ys = y/96.f; - f32 s = Min(xs, ys); - return(s); -} - -#define BASE_W 800 -#define BASE_H 600 - -internal b32 -LinuxX11WindowInit(int argc, char** argv, int* window_width, int* window_height){ - if (plat_settings.set_window_size){ - *window_width = plat_settings.window_w; - *window_height = plat_settings.window_h; - } else { - f32 schange = size_change(linuxvars.dpi_x, linuxvars.dpi_y); - *window_width = ceil32(BASE_W * schange); - *window_height = ceil32(BASE_H * schange); - } - *window_width = Max(*window_width, 1); - *window_height = Max(*window_height, 1); - - if (!GLXCanUseFBConfig(linuxvars.XDisplay)){ - system_error_box("Your XServer's GLX version is too old. GLX 1.3+ is required."); - } - - glx_config_result Config = ChooseGLXConfig(linuxvars.XDisplay, DefaultScreen(linuxvars.XDisplay)); - if (!Config.Found){ - system_error_box("Could not get a matching GLX FBConfig. Check your OpenGL drivers are installed correctly."); - } - - XSetWindowAttributes swa = {}; - swa.backing_store = WhenMapped; - swa.event_mask = StructureNotifyMask; - swa.bit_gravity = NorthWestGravity; - swa.colormap = XCreateColormap(linuxvars.XDisplay, RootWindow(linuxvars.XDisplay, Config.BestInfo.screen), Config.BestInfo.visual, AllocNone); - - u32 CWflags = CWBackingStore|CWBitGravity|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; - linuxvars.XWindow = XCreateWindow(linuxvars.XDisplay, RootWindow(linuxvars.XDisplay, Config.BestInfo.screen), 0, 0, *window_width, *window_height, 0, Config.BestInfo.depth, InputOutput, Config.BestInfo.visual, CWflags, &swa); - - if (!linuxvars.XWindow){ - system_error_box("XCreateWindow failed. Make sure your display is set up correctly."); - } - - //NOTE(inso): Set the window's type to normal - XChangeProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*)&linuxvars.atom__NET_WM_WINDOW_TYPE_NORMAL, 1); - - //NOTE(inso): window managers want the PID as a window property for some reason. - pid_t pid = getpid(); - XChangeProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&pid, 1); - - //NOTE(inso): set wm properties - XStoreName(linuxvars.XDisplay, linuxvars.XWindow, WINDOW_NAME); - - XSizeHints *sz_hints = XAllocSizeHints(); - XWMHints *wm_hints = XAllocWMHints(); - XClassHint *cl_hints = XAllocClassHint(); - - sz_hints->flags = PMinSize | PMaxSize | PWinGravity; - - sz_hints->min_width = 50; - sz_hints->min_height = 50; - - sz_hints->max_width = sz_hints->max_height = (1UL << 16UL); - - /* NOTE(inso): fluxbox thinks this is minimum, so don't set it - sz_hints->base_width = BASE_W; - sz_hints->base_height = BASE_H; - */ - sz_hints->win_gravity = NorthWestGravity; - - if (plat_settings.set_window_pos){ - sz_hints->flags |= USPosition; - sz_hints->x = plat_settings.window_x; - sz_hints->y = plat_settings.window_y; - } - - wm_hints->flags |= InputHint | StateHint; - wm_hints->input = True; - wm_hints->initial_state = NormalState; - - cl_hints->res_name = "4coder"; - cl_hints->res_class = "4coder"; - - char* win_name_list[] = { WINDOW_NAME }; - XTextProperty win_name; - XStringListToTextProperty(win_name_list, 1, &win_name); - - XSetWMProperties(linuxvars.XDisplay, linuxvars.XWindow, &win_name, NULL, argv, argc, sz_hints, wm_hints, cl_hints); - - XFree(win_name.value); - - XFree(sz_hints); - XFree(wm_hints); - XFree(cl_hints); - - linux_set_icon(linuxvars.XDisplay, linuxvars.XWindow); - - // NOTE(inso): make the window visible - XMapWindow(linuxvars.XDisplay, linuxvars.XWindow); - - GLXContext GLContext = - InitializeOpenGLContext(linuxvars.XDisplay, linuxvars.XWindow, &Config.BestConfig); - - XRaiseWindow(linuxvars.XDisplay, linuxvars.XWindow); - - if (plat_settings.set_window_pos){ - XMoveWindow(linuxvars.XDisplay, linuxvars.XWindow, plat_settings.window_x, plat_settings.window_y); - } - - if (plat_settings.maximize_window){ - linux_maximize_window(true); - } - else if (plat_settings.fullscreen_window){ - linux_toggle_fullscreen(); - } - - XSync(linuxvars.XDisplay, False); - - XWindowAttributes WinAttribs; - if (XGetWindowAttributes(linuxvars.XDisplay, linuxvars.XWindow, &WinAttribs)){ - *window_width = WinAttribs.width; - *window_height = WinAttribs.height; - } - - Atom wm_protos[] = { - linuxvars.atom_WM_DELETE_WINDOW, - linuxvars.atom__NET_WM_PING - }; - - XSetWMProtocols(linuxvars.XDisplay, linuxvars.XWindow, wm_protos, 2); - - return(true); -} - -internal void -linux_mod_flags_to_array(u32 state, b8 *mods){ - mods[MDFR_SHIFT_INDEX] = ((state & ShiftMask) != 0); - mods[MDFR_CONTROL_INDEX] = ((state & ControlMask) != 0); - mods[MDFR_CAPS_INDEX] = ((state & LockMask) != 0); - mods[MDFR_ALT_INDEX] = ((state & Mod1Mask) != 0); -} - -internal void -linux_handle_x11_events(void) -{ - static XEvent prev_event = {}; - b32 should_step = false; - - while (XPending(linuxvars.XDisplay)) - { - XEvent event; - XNextEvent(linuxvars.XDisplay, &event); - - if (XFilterEvent(&event, None) == True){ - continue; - } - - switch (event.type){ - case KeyPress: - { - should_step = true; - - b32 is_hold = (prev_event.type == KeyRelease && - prev_event.xkey.time == event.xkey.time && - prev_event.xkey.keycode == event.xkey.keycode); - - b8 mods[MDFR_INDEX_COUNT] = {}; - linux_mod_flags_to_array(event.xkey.state, mods); - linux_mod_flags_to_array(event.xkey.state, linuxvars.modifiers); - mods[MDFR_HOLD_INDEX] = is_hold; - - event.xkey.state &= ~(ControlMask); - - Status status; - KeySym keysym = NoSymbol; - u8 buff[32] = {}; - - Xutf8LookupString(linuxvars.input_context, &event.xkey, (char*)buff, sizeof(buff) - 1, &keysym, &status); - - if (status == XBufferOverflow){ - //TODO(inso): handle properly - Xutf8ResetIC(linuxvars.input_context); - XSetICFocus(linuxvars.input_context); - //LOG("FIXME: XBufferOverflow from LookupString.\n"); - } - - // don't push modifiers - if (keysym >= XK_Shift_L && keysym <= XK_Hyper_R){ - break; - } - - u32 key = utf8_to_u32_unchecked(buff); - u32 key_no_caps = key; - - if (mods[MDFR_CAPS_INDEX] && status == XLookupBoth && event.xkey.keycode){ - u8 buff_no_caps[32] = {}; - event.xkey.state &= ~(LockMask); - - Xutf8LookupString(linuxvars.input_context, &event.xkey, (char*)buff_no_caps, sizeof(buff_no_caps) - 1, NULL, &status); - - if (status == XBufferOverflow){ - //TODO(inso): handle properly - Xutf8ResetIC(linuxvars.input_context); - XSetICFocus(linuxvars.input_context); - //LOG("FIXME: XBufferOverflow from LookupString.\n"); - } - - if (*buff_no_caps){ - key_no_caps = utf8_to_u32_unchecked(buff_no_caps); - } - } - - if (key == '\r') key = '\n'; - if (key_no_caps == '\r') key_no_caps = '\n'; - - if (keysym == XK_ISO_Left_Tab){ - key = key_no_caps = '\t'; - mods[MDFR_SHIFT_INDEX] = 1; - } - - //Key_Code special_key = keycode_lookup_table[keysym]; - - Key_Code special_key = 0; - switch (keysym){ - case XK_BackSpace: - { - special_key = key_back; - }break; - - case XK_Delete: - { - special_key = key_del; - }break; - - case XK_Up: - { - special_key = key_up; - }break; - - case XK_Down: - { - special_key = key_down; - }break; - - case XK_Left: - { - special_key = key_left; - }break; - - case XK_Right: - { - special_key = key_right; - }break; - - case XK_Insert: - { - special_key = key_insert; - }break; - - case XK_Home: - { - special_key = key_home; - }break; - - case XK_End: - { - special_key = key_end; - }break; - - case XK_Page_Up: - { - special_key = key_page_up; - }break; - - case XK_Page_Down: - { - special_key = key_page_down; - }break; - - case XK_Escape: - { - special_key = key_esc; - }break; - - case XK_F1: - { - special_key = key_f1; - }break; - - case XK_F2: - { - special_key = key_f2; - }break; - - case XK_F3: - { - special_key = key_f3; - }break; - - case XK_F4: - { - special_key = key_f4; - }break; - - case XK_F5: - { - special_key = key_f5; - }break; - - case XK_F6: - { - special_key = key_f6; - }break; - - case XK_F7: - { - special_key = key_f7; - }break; - - case XK_F8: - { - special_key = key_f8; - }break; - - case XK_F9: - { - special_key = key_f9; - }break; - - case XK_F10: - { - special_key = key_f10; - }break; - - case XK_F11: - { - special_key = key_f11; - }break; - - case XK_F12: - { - special_key = key_f12; - }break; - - case XK_F13: - { - special_key = key_f13; - }break; - - case XK_F14: - { - special_key = key_f14; - }break; - - case XK_F15: - { - special_key = key_f15; - }break; - - case XK_F16: - { - special_key = key_f16; - }break; - } - - - if (special_key){ - LinuxPushKey(special_key, 0, 0, mods); - } - else if (key != 0){ - LinuxPushKey(key, key, key_no_caps, mods); - } - else{ - LinuxPushKey(0, 0, 0, mods); - } - }break; - - case KeyRelease: - { - should_step = true; - }break; - - case MotionNotify: - { - should_step = true; - linuxvars.input.mouse.x = event.xmotion.x; - linuxvars.input.mouse.y = event.xmotion.y; - linux_mod_flags_to_array(event.xmotion.state, linuxvars.modifiers); - }break; - - case ButtonPress: - { - should_step = true; - linux_mod_flags_to_array(event.xbutton.state, linuxvars.modifiers); - switch (event.xbutton.button){ - case Button1: - { - linuxvars.input.mouse.press_l = true; - linuxvars.input.mouse.l = true; - } break; - case Button3: - { - linuxvars.input.mouse.press_r = true; - linuxvars.input.mouse.r = true; - } break; - - // NOTE(inso): scroll up - case Button4: - { - linuxvars.input.mouse.wheel = -100; - }break; - - // NOTE(inso): scroll down - case Button5: - { - linuxvars.input.mouse.wheel = 100; - }break; - } - }break; - - case ButtonRelease: - { - should_step = true; - linux_mod_flags_to_array(event.xbutton.state, linuxvars.modifiers); - switch(event.xbutton.button){ - case Button1: - { - linuxvars.input.mouse.release_l = 1; - linuxvars.input.mouse.l = 0; - }break; - case Button3: - { - linuxvars.input.mouse.release_r = 1; - linuxvars.input.mouse.r = 0; - }break; - } - }break; - - case EnterNotify: - { - should_step = true; - linuxvars.input.mouse.out_of_window = 0; - }break; - - case LeaveNotify: - { - should_step = true; - linuxvars.input.mouse.out_of_window = 1; - }break; - - case FocusIn: - case FocusOut: - { - should_step = true; - linuxvars.input.mouse.l = 0; - linuxvars.input.mouse.r = 0; - }break; - - case ConfigureNotify: - { - should_step = true; - i32 w = event.xconfigure.width; - i32 h = event.xconfigure.height; - - if (w != target.width || h != target.height){ - LinuxResizeTarget(w, h); - } - }break; - - case MappingNotify: - { - if (event.xmapping.request == MappingModifier || event.xmapping.request == MappingKeyboard){ - XRefreshKeyboardMapping(&event.xmapping); - //LinuxKeycodeInit(linuxvars.XDisplay); - } - }break; - - case ClientMessage: - { - if ((Atom)event.xclient.data.l[0] == linuxvars.atom_WM_DELETE_WINDOW){ - should_step = true; - linuxvars.keep_running = 0; - } - else if ((Atom)event.xclient.data.l[0] == linuxvars.atom__NET_WM_PING){ - event.xclient.window = DefaultRootWindow(linuxvars.XDisplay); - XSendEvent( - linuxvars.XDisplay, - event.xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &event); - } - }break; - - // NOTE(inso): Someone wants us to give them the clipboard data. - case SelectionRequest: { - XSelectionRequestEvent request = event.xselectionrequest; - - XSelectionEvent response = {}; - response.type = SelectionNotify; - response.requestor = request.requestor; - response.selection = request.selection; - response.target = request.target; - response.time = request.time; - response.property = None; - - if (linuxvars.clipboard_outgoing.size && - request.selection == linuxvars.atom_CLIPBOARD && - request.property != None && - request.display && - request.requestor){ - Atom atoms[] = { - XA_STRING, - linuxvars.atom_UTF8_STRING - }; - - if (request.target == linuxvars.atom_TARGETS){ - XChangeProperty( - request.display, - request.requestor, - request.property, - XA_ATOM, - 32, - PropModeReplace, - (u8*)atoms, - ArrayCount(atoms)); - - response.property = request.property; - - } else { - b32 found = false; - for(int i = 0; i < ArrayCount(atoms); ++i){ - if (request.target == atoms[i]){ - found = true; - break; - } - } - - if (found){ - XChangeProperty( - request.display, - request.requestor, - request.property, - request.target, - 8, - PropModeReplace, - (u8*)linuxvars.clipboard_outgoing.str, - linuxvars.clipboard_outgoing.size - ); - - response.property = request.property; - } - } - } - - XSendEvent(request.display, request.requestor, True, 0, (XEvent*)&response); - - }break; - - // NOTE(inso): Another program is now the clipboard owner. - case SelectionClear: { - if (event.xselectionclear.selection == linuxvars.atom_CLIPBOARD){ - linuxvars.clipboard_outgoing.size = 0; - } - }break; - - // NOTE(inso): A program is giving us the clipboard data we asked for. - case SelectionNotify: { - XSelectionEvent* e = (XSelectionEvent*)&event; - if (e->selection == linuxvars.atom_CLIPBOARD && e->target == linuxvars.atom_UTF8_STRING && e->property != None){ - Atom type; - int fmt; - unsigned long nitems; - unsigned long bytes_left; - u8 *data; - - -#define PASTE_MAX_WORDS 0x20000000L - int result = XGetWindowProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD, 0L, PASTE_MAX_WORDS, False, linuxvars.atom_UTF8_STRING, &type, &fmt, &nitems, &bytes_left, &data); - - if (result == Success && fmt == 8){ - LinuxStringDup(&linuxvars.clipboard_contents, data, nitems); - should_step = true; - linuxvars.new_clipboard = true; - XFree(data); - XDeleteProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD); - } - } - }break; - - case Expose: - case VisibilityNotify: { - should_step = true; - }break; - - default: - { - if (event.type == linuxvars.xfixes_selection_event){ - XFixesSelectionNotifyEvent* sne = (XFixesSelectionNotifyEvent*)&event; - if (sne->subtype == XFixesSelectionNotify && sne->owner != linuxvars.XWindow){ - XConvertSelection(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.atom_UTF8_STRING, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); - } - } - }break; - } - - prev_event = event; - } - - if (should_step){ - system_schedule_step(); - } -} - -#include "4ed_link_system_functions.cpp" -#include "4ed_shared_init_logic.cpp" - -int -main(int argc, char **argv){ - // - // System Linkage - // - - link_system_code(); - - // - // Memory init - // - - memset(&linuxvars, 0, sizeof(linuxvars)); - memset(&target, 0, sizeof(target)); - memset(&memory_vars, 0, sizeof(memory_vars)); - memset(&plat_settings, 0, sizeof(plat_settings)); - - memset(&libraries, 0, sizeof(libraries)); - memset(&app, 0, sizeof(app)); - memset(&custom_api, 0, sizeof(custom_api)); - - memory_init(); - - // - // HACK(allen): - // Previously zipped stuff is here, it should be zipped in the new pattern now. - // - - init_shared_vars(); - - linuxvars.track_table_size = KB(16); - linuxvars.track_table = system_memory_allocate(linuxvars.track_table_size); - - linuxvars.track_node_size = KB(16); - void *track_nodes = system_memory_allocate(linuxvars.track_node_size); - - i32 track_result = init_track_system(&linuxvars.track, &shared_vars.scratch, - linuxvars.track_table, linuxvars.track_table_size, - track_nodes, linuxvars.track_node_size); - - if (track_result != FileTrack_Good){ - exit(1); - } - - // - // Load Core Code - // - load_app_code(); - - // - // Read command line - // - read_command_line(argc, argv); - - // - // Load Custom Code - // -#if defined(FRED_SUPER) - load_custom_code(); -#else - DontCompile; -#endif - - // - // Threads - // - - work_system_init(); - - // - // Coroutines - // - - coroutines_init(); - - // - // X11 init - // - - linuxvars.XDisplay = XOpenDisplay(0); - if (!linuxvars.XDisplay){ - // NOTE(inso): probably not worth trying the popup in this case... - //LOG("Can't open display!\n"); - exit(1); - } - -#define LOAD_ATOM(x) linuxvars.atom_##x = XInternAtom(linuxvars.XDisplay, #x, False); - - LOAD_ATOM(TARGETS); - LOAD_ATOM(CLIPBOARD); - LOAD_ATOM(UTF8_STRING); - LOAD_ATOM(_NET_WM_STATE); - LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); - LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); - LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); - LOAD_ATOM(_NET_WM_PING); - LOAD_ATOM(_NET_WM_WINDOW_TYPE); - LOAD_ATOM(_NET_WM_WINDOW_TYPE_NORMAL); - LOAD_ATOM(_NET_WM_PID); - LOAD_ATOM(WM_DELETE_WINDOW); - -#undef LOAD_ATOM - - linuxvars.dpi_x = linuxvars.dpi_y = LinuxGetXSettingsDPI(linuxvars.XDisplay, DefaultScreen(linuxvars.XDisplay)); - - // fallback - if (linuxvars.dpi_x == -1){ - int scr = DefaultScreen(linuxvars.XDisplay); - - int dw = DisplayWidth(linuxvars.XDisplay, scr); - int dh = DisplayHeight(linuxvars.XDisplay, scr); - - int dw_mm = DisplayWidthMM(linuxvars.XDisplay, scr); - int dh_mm = DisplayHeightMM(linuxvars.XDisplay, scr); - - linuxvars.dpi_x = dw_mm ? dw / (dw_mm / 25.4) : 96; - linuxvars.dpi_y = dh_mm ? dh / (dh_mm / 25.4) : 96; - - //LOGF("%dx%d - %dmmx%dmm DPI: %dx%d\n", dw, dh, dw_mm, dh_mm, linuxvars.dpi_x, linuxvars.dpi_y); - } - else{ - //LOGF("DPI from XSETTINGS: %d\n", linuxvars.dpi_x); - } - - int window_width, window_height; - if (!LinuxX11WindowInit(argc, argv, &window_width, &window_height)){ - return 1; - } - - int xfixes_version_unused, xfixes_err_unused; - b32 xquery_extension_r = XQueryExtension(linuxvars.XDisplay, "XFIXES", &xfixes_version_unused, &linuxvars.xfixes_selection_event, &xfixes_err_unused); - linuxvars.has_xfixes = (xquery_extension_r == True); - - if (linuxvars.has_xfixes){ - XFixesSelectSelectionInput(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask); - } - else{ - //LOG("Your X server doesn't support XFIXES, mention this fact if you report any clipboard-related issues.\n"); - } - - Init_Input_Result input_result = LinuxInputInit(linuxvars.XDisplay, linuxvars.XWindow); - - linuxvars.input_method = input_result.input_method; - linuxvars.input_style = input_result.best_style; - linuxvars.input_context = input_result.xic; - - //LinuxKeycodeInit(linuxvars.XDisplay); - - Cursor xcursors[APP_MOUSE_CURSOR_COUNT] = { - None, - None, - XCreateFontCursor(linuxvars.XDisplay, XC_xterm), - XCreateFontCursor(linuxvars.XDisplay, XC_sb_h_double_arrow), - XCreateFontCursor(linuxvars.XDisplay, XC_sb_v_double_arrow) - }; - - { - char data = 0; - XColor c = {}; - Pixmap p = XCreateBitmapFromData(linuxvars.XDisplay, linuxvars.XWindow, &data, 1, 1); - - linuxvars.hidden_cursor = XCreatePixmapCursor(linuxvars.XDisplay, p, p, &c, &c, 0, 0); - - XFreePixmap(linuxvars.XDisplay, p); - } - - - // - // Font System Init - // - - Partition *scratch = &shared_vars.scratch; - Temp_Memory temp = begin_temp_memory(scratch); - Font_Setup_List font_setup = system_font_get_local_stubs(scratch); - linux_get_loadable_fonts(scratch, &font_setup); - system_font_init(&sysfunc.font, plat_settings.font_size, plat_settings.use_hinting, font_setup); - end_temp_memory(temp); - - // - // Epoll init - // - - linuxvars.x11_fd = ConnectionNumber(linuxvars.XDisplay); - linuxvars.inotify_fd = inotify_init1(IN_NONBLOCK); - linuxvars.step_event_fd = eventfd(0, EFD_NONBLOCK); - linuxvars.step_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); - - linuxvars.epoll = epoll_create(16); - - { - struct epoll_event e = {}; - e.events = EPOLLIN | EPOLLET; - - e.data.u64 = LINUX_4ED_EVENT_X11; - epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.x11_fd, &e); - - e.data.u64 = LINUX_4ED_EVENT_STEP; - epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_event_fd, &e); - - e.data.u64 = LINUX_4ED_EVENT_STEP_TIMER; - epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_timer_fd, &e); - } - - // - // App init - // - - XAddConnectionWatch(linuxvars.XDisplay, &LinuxX11ConnectionWatch, NULL); - - char cwd[4096]; - u32 size = sysfunc.get_current_path(cwd, sizeof(cwd)); - if (size == 0 || size >= sizeof(cwd)){ - system_error_box("Could not get current directory at launch."); - } - String curdir = make_string(cwd, size); - terminate_with_null(&curdir); - replace_char(&curdir, '\\', '/'); - - //LOG("Initializing application variables\n"); - app.init(&sysfunc, &target, &memory_vars, linuxvars.clipboard_contents, curdir, custom_api); - - LinuxResizeTarget(window_width, window_height); - - // - // Main loop - // - - system_acquire_lock(FRAME_LOCK); - - system_schedule_step(); - - linuxvars.keep_running = true; - linuxvars.input.first_step = true; - linuxvars.input.dt = (frame_useconds / 1000000.f); - - for (;;){ - if (XEventsQueued(linuxvars.XDisplay, QueuedAlready)){ - linux_handle_x11_events(); - } - - system_release_lock(FRAME_LOCK); - - struct epoll_event events[16]; - int num_events = epoll_wait(linuxvars.epoll, events, ArrayCount(events), -1); - - system_acquire_lock(FRAME_LOCK); - - if (num_events == -1){ - if (errno != EINTR){ - //LOG("epoll_wait\n"); - } - continue; - } - - b32 do_step = false; - - for (int i = 0; i < num_events; ++i){ - int fd = events[i].data.u64 & UINT32_MAX; - u64 type = events[i].data.u64 & ~fd; - - switch (type){ - case LINUX_4ED_EVENT_X11: - { - linux_handle_x11_events(); - } break; - - case LINUX_4ED_EVENT_X11_INTERNAL: { - XProcessInternalConnection(linuxvars.XDisplay, fd); - } break; - - case LINUX_4ED_EVENT_STEP: { - u64 ev; - int ret; - do { - ret = read(linuxvars.step_event_fd, &ev, 8); - } while (ret != -1 || errno != EAGAIN); - do_step = true; - } break; - - case LINUX_4ED_EVENT_STEP_TIMER: { - u64 count; - int ret; - do { - ret = read(linuxvars.step_timer_fd, &count, 8); - } while (ret != -1 || errno != EAGAIN); - do_step = true; - } break; - - case LINUX_4ED_EVENT_CLI: { - system_schedule_step(); - } break; - } - } - - if (do_step){ - linuxvars.last_step = system_now_time(); - - // NOTE(allen): Frame Clipboard Input - if (linuxvars.input.first_step || !linuxvars.has_xfixes){ - XConvertSelection(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.atom_UTF8_STRING, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); - } - - if (linuxvars.new_clipboard){ - linuxvars.input.clipboard = linuxvars.clipboard_contents; - linuxvars.new_clipboard = 0; - } - else{ - linuxvars.input.clipboard = null_string; - } - - Application_Step_Input frame_input = linuxvars.input; - frame_input.trying_to_kill = !linuxvars.keep_running; - memcpy(frame_input.keys.modifiers, linuxvars.modifiers, sizeof(frame_input.keys.modifiers)); - - // HACK(allen): THIS SHIT IS FUCKED (happens on mac too) - b32 keep_running = linuxvars.keep_running; - - // NOTE(allen): Application Core Update - target.buffer.pos = 0; - Application_Step_Result result = {}; - if (app.step != 0){ - result = app.step(&sysfunc, &target, &memory_vars, &frame_input); - } - else{ - //LOG("app.step == 0 -- skipping\n"); - } - - - // NOTE(allen): Finish the Loop - if (result.perform_kill){ - break; - } - else if (!keep_running && !linuxvars.keep_running){ - linuxvars.keep_running = true; - } - - // NOTE(NAME): Switch to New Title - if (result.has_new_title){ - XStoreName(linuxvars.XDisplay, linuxvars.XWindow, result.title_string); - } - - // NOTE(allen): Switch to New Cursor - if (result.mouse_cursor_type != linuxvars.cursor && !linuxvars.input.mouse.l){ - Cursor c = xcursors[result.mouse_cursor_type]; - if (!linuxvars.hide_cursor){ - XDefineCursor(linuxvars.XDisplay, linuxvars.XWindow, c); - } - linuxvars.cursor = result.mouse_cursor_type; - } - - // NOTE(allen): Render - gl_render(&target, &shared_vars.pixel_scratch); - glXSwapBuffers(linuxvars.XDisplay, linuxvars.XWindow); - - // NOTE(allen): Toggle Full Screen - if (linuxvars.do_toggle){ - linux_toggle_fullscreen(); - linuxvars.do_toggle = false; - } - - // NOTE(allen): Schedule Another Step if Needed - if (result.animating){ - system_schedule_step(); - } - - flush_thread_group(BACKGROUND_THREADS); - - // TODO(allen): CROSS REFERENCE WITH WIN32 "TIC898989" - memset(&linuxvars.input.keys, 0, sizeof(linuxvars.input.keys)); - linuxvars.input.mouse.press_l = 0; - linuxvars.input.mouse.release_l = 0; - linuxvars.input.mouse.press_r = 0; - linuxvars.input.mouse.release_r = 0; - linuxvars.input.mouse.wheel = 0; - - linuxvars.input.first_step = 0; - } - } - - if (linuxvars.XDisplay){ - if (linuxvars.XWindow){ - XDestroyWindow(linuxvars.XDisplay, linuxvars.XWindow); - } - XCloseDisplay(linuxvars.XDisplay); - } - - return(0); -} - -#include "linux_4ed_file_track.cpp" - -// BOTTOM -// vim: expandtab:ts=4:sts=4:sw=4 - diff --git a/platform_linux/linux_4ed_old.cpp b/platform_linux/linux_4ed_old.cpp new file mode 100644 index 00000000..81ec6464 --- /dev/null +++ b/platform_linux/linux_4ed_old.cpp @@ -0,0 +1,2286 @@ +/* + * Mr. 4th Dimention - Allen Webster + * (Mostly by insofaras) + * + * 14.11.2015 + * + * Linux layer for 4coder + * + */ + +// TOP + +#define IS_PLAT_LAYER + +#include +#include "4coder_base_types.h" +#include "4coder_API/4coder_version.h" + +#include "4coder_lib/4coder_utf8.h" + +#if defined(FRED_SUPER) +#include "4coder_keycode_extension.h" +# include "4coder_API/4coder_style.h" + +# include "4coder_lib/4coder_arena.h" +# include "4coder_lib/4coder_arena.cpp" +# define FSTRING_IMPLEMENTATION +# include "4coder_lib/4coder_string.h" + +# include "4coder_API/4coder_types.h" + +#else +# include "4coder_default_bindings.cpp" +#endif + +#include "4ed_math.h" + +#include "4ed_font.h" +#include "4ed_system.h" +#include "4ed_render_target.h" +#include "4ed_render_format.h" +#include "4ed.h" +#include "4ed_linked_node_macros.h" + +#include "4ed_file_track.h" +#include "4ed_system_shared.h" + +#include "unix_4ed_headers.h" +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "4ed_shared_thread_constants.h" +#include "unix_threading_wrapper.h" +#include "linux_semaphore_wrapper.h" + +// +// Linux macros +// + +#define FPS 60L +#define frame_useconds (1000000UL / FPS) + +#define LINUX_FN_DEBUG(fmt, ...) do { \ + /*LOGF("%s: " fmt "\n", __func__, ##__VA_ARGS__);*/ \ +} while (0) + +// TODO(allen): Make an intrinsics header that uses the cracked OS to define a single set of intrinsic names. +#define InterlockedCompareExchange(dest, ex, comp) \ +__sync_val_compare_and_swap((dest), (comp), (ex)) + +// +// Linux structs / enums +// + +enum{ + LINUX_4ED_EVENT_X11 = (UINT64_C(1) << 32), + LINUX_4ED_EVENT_X11_INTERNAL = (UINT64_C(2) << 32), + LINUX_4ED_EVENT_STEP = (UINT64_C(3) << 32), + LINUX_4ED_EVENT_STEP_TIMER = (UINT64_C(4) << 32), + LINUX_4ED_EVENT_CLI = (UINT64_C(5) << 32), +}; + +// +// Linux forward declarations +// + +internal void LinuxStringDup(String*, void*, size_t); + +//////////////////////////////// + +#define SLASH '/' +#define DLL "so" + +global System_Functions sysfunc; +#include "4ed_shared_library_constants.h" +#include "unix_library_wrapper.h" + +#include "4ed_standard_libraries.cpp" +#include "4ed_coroutine.cpp" +#include "4ed_font.cpp" + +//////////////////////////////// + +struct Linux_Vars{ + Display *XDisplay; + Window XWindow; + + XIM input_method; + XIMStyle input_style; + XIC input_context; + + Application_Step_Input input; + b8 modifiers[MDFR_INDEX_COUNT]; + + String clipboard_contents; + String clipboard_outgoing; + b32 new_clipboard; + + Atom atom_TARGETS; + Atom atom_CLIPBOARD; + Atom atom_UTF8_STRING; + Atom atom__NET_WM_STATE; + Atom atom__NET_WM_STATE_MAXIMIZED_HORZ; + Atom atom__NET_WM_STATE_MAXIMIZED_VERT; + Atom atom__NET_WM_STATE_FULLSCREEN; + Atom atom__NET_WM_PING; + Atom atom__NET_WM_WINDOW_TYPE; + Atom atom__NET_WM_WINDOW_TYPE_NORMAL; + Atom atom__NET_WM_PID; + Atom atom_WM_DELETE_WINDOW; + + b32 has_xfixes; + int xfixes_selection_event; + + int epoll; + + int step_timer_fd; + int step_event_fd; + int x11_fd; + int inotify_fd; + + u64 last_step; + + b32 full_screen; + b32 do_toggle; + b32 keep_running; + + Application_Mouse_Cursor cursor; + b32 hide_cursor; + Cursor hidden_cursor; + + sem_t thread_semaphore; + + i32 dpi_x, dpi_y; + + b32 vsync; + + File_Track_System track; + void *track_table; + u32 track_table_size; + u32 track_node_size; +}; + +//////////////////////////////// + +global Linux_Vars linuxvars; +global Render_Target target; +global Application_Memory memory_vars; +global Plat_Settings plat_settings; + +global Libraries libraries; +global App_Functions app; +global Custom_API custom_api; + +global Coroutine_System_Auto_Alloc coroutines; + +//////////////////////////////// + +#include "linux_icon.h" +internal void +linux_set_icon(Display* d, Window w){ + Atom WM_ICON = XInternAtom(d, "_NET_WM_ICON", False); + XChangeProperty(d, w, WM_ICON, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)linux_icon, sizeof(linux_icon) / sizeof(long)); +} + +#include "linux_error_box.cpp" + +//////////////////////////////// + +internal sem_t* +handle_sem(Plat_Handle h){ + return(*(sem_t**)&h); +} + +internal Plat_Handle +handle_sem(sem_t *sem){ + return(*(Plat_Handle*)&sem); +} + +//////////////////////////////// + +internal +Sys_Get_4ed_Path_Sig(system_get_4ed_path){ + ssize_t size = readlink("/proc/self/exe", out, capacity - 1); + if (size != -1 && size < capacity - 1){ + String str = make_string(out, size); + remove_last_folder(&str); + terminate_with_null(&str); + size = str.size; + } + else{ + size = 0; + } + return(size); +} + +#include "unix_4ed_functions.cpp" + +internal +Sys_Now_Time_Sig(system_now_time){ + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + u64 result = (spec.tv_sec*UINT64_C(1000000)) + (spec.tv_nsec/UINT64_C(1000)); + return(result); +} + +//////////////////////////////// + +internal void +system_schedule_step(){ + u64 now = system_now_time(); + u64 diff = (now - linuxvars.last_step); + if (diff > (u64)frame_useconds){ + u64 ev = 1; + ssize_t size = write(linuxvars.step_event_fd, &ev, sizeof(ev)); + AllowLocal(size); + } + else{ + struct itimerspec its = {}; + timerfd_gettime(linuxvars.step_timer_fd, &its); + if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0){ + its.it_value.tv_nsec = (frame_useconds - diff) * 1000UL; + timerfd_settime(linuxvars.step_timer_fd, 0, &its, NULL); + } + } +} + +//////////////////////////////// + +#include "4ed_work_queues.cpp" + +//////////////////////////////// + +internal void +linux_set_wm_state(Display* d, Window w, Atom one, Atom two, int mode){ + //NOTE(inso): this will only work after it is mapped + + enum { STATE_REMOVE, STATE_ADD, STATE_TOGGLE }; + + XEvent e = {}; + + e.xany.type = ClientMessage; + e.xclient.message_type = linuxvars.atom__NET_WM_STATE; + e.xclient.format = 32; + e.xclient.window = w; + e.xclient.data.l[0] = mode; + e.xclient.data.l[1] = one; + e.xclient.data.l[2] = two; + e.xclient.data.l[3] = 1L; + + XSendEvent(d, RootWindow(d, 0), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); +} + +internal void +linux_maximize_window(b32 maximize){ + linux_set_wm_state(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_STATE_MAXIMIZED_HORZ, linuxvars.atom__NET_WM_STATE_MAXIMIZED_VERT, maximize?1:0); +} + +internal void +linux_toggle_fullscreen(){ + linuxvars.full_screen = !linuxvars.full_screen; + linux_set_wm_state(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_STATE_FULLSCREEN, 0, linuxvars.full_screen?1:0); +} + +internal +Sys_Show_Mouse_Cursor_Sig(system_show_mouse_cursor){ + linuxvars.hide_cursor = !show; + XDefineCursor(linuxvars.XDisplay, linuxvars.XWindow, show?None:linuxvars.hidden_cursor); +} + +internal +Sys_Set_Fullscreen_Sig(system_set_fullscreen){ + b32 success = true; + linuxvars.do_toggle = (linuxvars.full_screen != full_screen); + return(success); +} + +internal +Sys_Is_Fullscreen_Sig(system_is_fullscreen){ + b32 result = (linuxvars.full_screen != linuxvars.do_toggle); + return(result); +} + +#include "4ed_coroutine_functions.cpp" + +#include "4ed_system_shared.cpp" + +// +// File Change Listener +// + +internal b32 +handle_track_out_of_memory(i32 val){ + b32 result = false; + + switch (val){ + case FileTrack_OutOfTableMemory: + { + u32 new_table_size = linuxvars.track_table_size*2; + void *new_table = system_memory_allocate(new_table_size); + move_track_system(&linuxvars.track, &shared_vars.scratch, new_table, new_table_size); + system_memory_free(linuxvars.track_table, linuxvars.track_table_size); + linuxvars.track_table_size = new_table_size; + linuxvars.track_table = new_table; + }break; + + case FileTrack_OutOfListenerMemory: + { + linuxvars.track_node_size *= 2; + void *node_expansion = system_memory_allocate(linuxvars.track_node_size); + expand_track_system_listeners(&linuxvars.track, &shared_vars.scratch, node_expansion, linuxvars.track_node_size); + }break; + + default: result = true; break; + } + + return(result); +} + +internal +Sys_Add_Listener_Sig(system_add_listener){ + b32 result = false; + for (;;){ + i32 track_result = add_listener(&linuxvars.track, &shared_vars.scratch, (u8*)filename); + if (handle_track_out_of_memory(track_result)){ + if (track_result == FileTrack_Good){ + result = true; + } + break; + } + } + return(result); +} + +internal +Sys_Remove_Listener_Sig(system_remove_listener){ + b32 result = false; + i32 track_result = remove_listener(&linuxvars.track, &shared_vars.scratch, (u8*)filename); + if (track_result == FileTrack_Good){ + result = true; + } + return(result); +} + +internal +Sys_Get_File_Change_Sig(system_get_file_change){ + b32 result = false; + + i32 size = 0; + i32 get_result = get_change_event(&linuxvars.track, &shared_vars.scratch, (u8*)buffer, max, &size); + + *required_size = size; + *mem_too_small = false; + if (get_result == FileTrack_Good){ + result = true; + } + else if (get_result == FileTrack_MemoryTooSmall){ + *mem_too_small = true; + result = true; + } + + return(result); +} + +// +// Clipboard +// + +internal +Sys_Post_Clipboard_Sig(system_post_clipboard){ + LinuxStringDup(&linuxvars.clipboard_outgoing, str.str, str.size); + XSetSelectionOwner(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); +} + +// +// CLI +// + +internal +Sys_CLI_Call_Sig(system_cli_call, path, script_name, cli_out){ + LINUX_FN_DEBUG("%s %s", path, script_name); + + int pipe_fds[2]; + if (pipe(pipe_fds) == -1){ + perror("system_cli_call: pipe"); + return 0; + } + + pid_t child_pid = fork(); + if (child_pid == -1){ + perror("system_cli_call: fork"); + return 0; + } + + enum { PIPE_FD_READ, PIPE_FD_WRITE }; + + // child + if (child_pid == 0){ + close(pipe_fds[PIPE_FD_READ]); + dup2(pipe_fds[PIPE_FD_WRITE], STDOUT_FILENO); + dup2(pipe_fds[PIPE_FD_WRITE], STDERR_FILENO); + + if (chdir(path) == -1){ + perror("system_cli_call: chdir"); + exit(1); + } + + char* argv[] = { "sh", "-c", script_name, NULL }; + + if (execv("/bin/sh", argv) == -1){ + perror("system_cli_call: execv"); + } + exit(1); + } + else{ + close(pipe_fds[PIPE_FD_WRITE]); + + *(pid_t*)&cli_out->proc = child_pid; + *(int*)&cli_out->out_read = pipe_fds[PIPE_FD_READ]; + *(int*)&cli_out->out_write = pipe_fds[PIPE_FD_WRITE]; + + struct epoll_event e = {}; + e.events = EPOLLIN | EPOLLET; + e.data.u64 = LINUX_4ED_EVENT_CLI; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, pipe_fds[PIPE_FD_READ], &e); + } + + return(true); +} + +internal +Sys_CLI_Begin_Update_Sig(system_cli_begin_update){ + // NOTE(inso): I don't think anything needs to be done here. +} + +internal +Sys_CLI_Update_Step_Sig(system_cli_update_step){ + int pipe_read_fd = *(int*)&cli->out_read; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipe_read_fd, &fds); + + struct timeval tv = {}; + + size_t space_left = max; + char* ptr = dest; + + while (space_left > 0 && select(pipe_read_fd + 1, &fds, NULL, NULL, &tv) == 1){ + ssize_t num = read(pipe_read_fd, ptr, space_left); + if (num == -1){ + perror("system_cli_update_step: read"); + } else if (num == 0){ + // NOTE(inso): EOF + break; + } else { + ptr += num; + space_left -= num; + } + } + + *amount = (ptr - dest); + return((ptr - dest) > 0); +} + +internal +Sys_CLI_End_Update_Sig(system_cli_end_update){ + pid_t pid = *(pid_t*)&cli->proc; + b32 close_me = false; + + int status; + if (pid && waitpid(pid, &status, WNOHANG) > 0){ + cli->exit = WEXITSTATUS(status); + + close_me = true; + close(*(int*)&cli->out_read); + close(*(int*)&cli->out_write); + + struct epoll_event e = {}; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_DEL, *(int*)&cli->out_read, &e); + } + + return(close_me); +} + +#include "4ed_font_provider_freetype.h" +global u32 system_font_method = SystemFontMethod_FilePath; +#include "4ed_font_provider_freetype.cpp" + +#undef internal +#include +#define internal static + +global FcConfig *fc_config = 0; + +internal +Sys_Font_Path(name, parameters){ + if (fc_config == 0){ + fc_config = FcInitLoadConfigAndFonts(); + } + + Font_Path path = {}; + + FcPattern *pattern_regular = FcPatternBuild( + 0, + FC_POSTSCRIPT_NAME, FcTypeString, name, + FC_SIZE, FcTypeDouble, (double)parameters->pt_size, + FC_FONTFORMAT, FcTypeString, "TrueType", + FC_STYLE, FcTypeString, (FcChar8*)"Regular", + (void*)0); + + FcPattern *pattern_styled = 0; + if (parameters->italics || parameters->bold){ + if (parameters->italics && !parameters->bold){ + pattern_styled = FcPatternBuild( + 0, + FC_POSTSCRIPT_NAME, FcTypeString, name, + FC_SIZE, FcTypeDouble, (double)parameters->pt_size, + FC_FONTFORMAT, FcTypeString, "TrueType", + FC_STYLE, FcTypeString, (FcChar8*)"Italic", + (void*)0); + } + else if (!parameters->italics && parameters->bold){ + pattern_styled = FcPatternBuild( + 0, + FC_POSTSCRIPT_NAME, FcTypeString, name, + FC_SIZE, FcTypeDouble, (double)parameters->pt_size, + FC_FONTFORMAT, FcTypeString, "TrueType", + FC_STYLE, FcTypeString, (FcChar8*)"Bold", + (void*)0); + } + else{ + pattern_styled = FcPatternBuild( + 0, + FC_POSTSCRIPT_NAME, FcTypeString, name, + FC_SIZE, FcTypeDouble, (double)parameters->pt_size, + FC_FONTFORMAT, FcTypeString, "TrueType", + FC_STYLE, FcTypeString, (FcChar8*)"Bold Italic", + (void*)0); + } + } + + Partition *part = &shared_vars.font_scratch; + path.temp = begin_temp_memory(part); + + b32 got_font = false; + if (pattern_styled != 0){ + if (FcConfigSubstitute(fc_config, pattern_styled, FcMatchPattern)){ + FcDefaultSubstitute(pattern_styled); + + FcResult res; + FcPattern *font = FcFontMatch(fc_config, pattern_styled, &res); + FcChar8 *filename = 0; + + if (font != 0){ + FcPatternGetString(font, FC_FILE, 0, &filename); + if (filename != 0){ + i32 len = str_size((char*)filename); + char *buffer = push_array(part, char, len + 1); + if (buffer == 0){ + sysshared_partition_grow(part, l_round_up_i32(len + 1, KB(4))); + buffer = push_array(part, char, len + 1); + } + + if (buffer != 0){ + push_align(part, 8); + memcpy(buffer, filename, len + 1); + path.len = len; + path.name = buffer; + } + + got_font = true; + } + FcPatternDestroy(font); + } + } + + FcPatternDestroy(pattern_styled); + pattern_styled = 0; + } + + if (!got_font){ + if (FcConfigSubstitute(fc_config, pattern_regular, FcMatchPattern)){ + FcDefaultSubstitute(pattern_regular); + + FcResult res; + FcPattern *font = FcFontMatch(fc_config, pattern_regular, &res); + FcChar8 *filename = 0; + + if (font != 0){ + FcPatternGetString(font, FC_FILE, 0, &filename); + if (filename != 0){ + i32 len = str_size((char*)filename); + char *buffer = push_array(part, char, len + 1); + if (buffer == 0){ + sysshared_partition_grow(part, l_round_up_i32(len + 1, KB(4))); + buffer = push_array(part, char, len + 1); + } + + if (buffer != 0){ + push_align(part, 8); + memcpy(buffer, filename, len + 1); + path.len = len; + path.name = buffer; + } + + got_font = true; + } + FcPatternDestroy(font); + } + } + + FcPatternDestroy(pattern_styled); + } + + FcPatternDestroy(pattern_regular); + + return(path); +} + +Sys_Font_Data_Not_Used; + +internal char* +linux_get_loadable_fonts(Partition *part, Font_Setup_List *list){ + if (fc_config == 0){ + fc_config = FcInitLoadConfigAndFonts(); + } + + FcPattern* pat = FcPatternBuild( + 0, + FC_STYLE, FcTypeString, (FcChar8*)"Regular", + (void*)0); + FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_FILE, (char*)0); + FcFontSet* fs = FcFontList(fc_config, pat, os); + if (fs != 0){ + //LOGF("Total matching fonts: %d\n", fs->nfont); + for (int i=0; fs && i < fs->nfont; ++i) { + FcPattern* font = fs->fonts[i]; + FcChar8 *file = 0; + FcChar8 *style = 0; + FcChar8 *family = 0; + + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch && + FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch) + { + Temp_Memory reset = begin_temp_memory(part); + Font_Setup *setup = push_array(part, Font_Setup, 1); + if (setup != 0){ + memset(setup, 0, sizeof(*setup)); + + i32 len = str_size((char*)file); + if (len < sizeof(setup->stub.name)){ + i32 name_len = str_size((char*)family); + if (name_len < sizeof(setup->name)){ + setup->stub.load_from_path = true; + memcpy(setup->stub.name, file, len + 1); + setup->stub.len = len; + setup->has_display_name = true; + setup->len = name_len; + memcpy(setup->name, family, name_len + 1); + sll_push(list->first, list->last, setup); + } + else{ + end_temp_memory(reset); + } + } + else{ + end_temp_memory(reset); + } + } + } + } + FcFontSetDestroy(fs); + } +} + +#include +#include "opengl/4ed_opengl_render.cpp" + +// +// End of system funcs +// + +// +// Renderer +// + +internal void +LinuxResizeTarget(i32 width, i32 height){ + if (width > 0 && height > 0){ + target.width = width; + target.height = height; + } +} + +// +// OpenGL init +// + +// NOTE(allen): Thanks to Casey for providing the linux OpenGL launcher. +static bool ctxErrorOccurred = false; + +internal int +ctxErrorHandler( Display *dpy, XErrorEvent *ev ){ + ctxErrorOccurred = true; + return 0; +} + +typedef GLXContext (glXCreateContextAttribsARB_Function)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +typedef void (glXSwapIntervalEXT_Function) (Display *dpy, GLXDrawable drawable, int interval); +typedef int (glXSwapIntervalMESA_Function) (unsigned int interval); +typedef int (glXGetSwapIntervalMESA_Function) (void); +typedef int (glXSwapIntervalSGI_Function) (int interval); + +internal GLXContext +InitializeOpenGLContext(Display *XDisplay, Window XWindow, GLXFBConfig *best_config){ + + const char *glxExts = glXQueryExtensionsString(XDisplay, DefaultScreen(XDisplay)); + + glXCreateContextAttribsARB_Function *glXCreateContextAttribsARB = 0; + glXSwapIntervalEXT_Function *glXSwapIntervalEXT = 0; + glXSwapIntervalMESA_Function *glXSwapIntervalMESA = 0; + glXGetSwapIntervalMESA_Function *glXGetSwapIntervalMESA = 0; + glXSwapIntervalSGI_Function *glXSwapIntervalSGI = 0; + +#define GLXLOAD(f) f = (f##_Function*) glXGetProcAddressARB((const GLubyte*) #f); + GLXLOAD(glXCreateContextAttribsARB); + + GLXContext ctx = 0; + ctxErrorOccurred = false; + int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler); + + if (glXCreateContextAttribsARB == 0){ + //LOG("glXCreateContextAttribsARB() not found, using old-style GLX context\n" ); + ctx = glXCreateNewContext( XDisplay, *best_config, GLX_RGBA_TYPE, 0, True ); + } + else{ + int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 2, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, +#if defined(FRED_INTERNAL) + GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB, +#endif + None + }; + + //LOG("Creating GL 2.1 context... "); + ctx = glXCreateContextAttribsARB(XDisplay, *best_config, 0, True, context_attribs); + + XSync( XDisplay, False ); + if (!ctxErrorOccurred && ctx){ + //LOG("Created GL 2.1 context.\n"); + } + else{ + //LOG("Could not create a context.\n"); + exit(1); + } + } + + XSync(XDisplay, False); + XSetErrorHandler(oldHandler); + + if (ctxErrorOccurred || !ctx){ + //LOG("Failed to create an OpenGL context\n"); + exit(1); + } + + b32 Direct; + if (!glXIsDirect(XDisplay, ctx)){ + //LOG("Indirect GLX rendering context obtained\n"); + Direct = false; + } + else{ + //LOG("Direct GLX rendering context obtained\n"); + Direct = true; + } + + //LOG("Making context current\n"); + glXMakeCurrent( XDisplay, XWindow, ctx ); + + //TODO(inso): glGetStringi is required in core profile if the GL version is >= 3.0 + //char *Extensions = (char *)glGetString(GL_EXTENSIONS); + + //NOTE(inso): enable vsync if available. this should probably be optional + if (Direct && strstr(glxExts, "GLX_EXT_swap_control ")){ + GLXLOAD(glXSwapIntervalEXT); + + if (glXSwapIntervalEXT != 0){ + glXSwapIntervalEXT(XDisplay, XWindow, 1); + + unsigned int swap_val = 0; + glXQueryDrawable(XDisplay, XWindow, GLX_SWAP_INTERVAL_EXT, &swap_val); + + linuxvars.vsync = (swap_val == true); + //LOGF("VSync enabled? %s.\n", linuxvars.vsync ? "Yes" : "No"); + } + + } + else if (Direct && strstr(glxExts, "GLX_MESA_swap_control ")){ + + GLXLOAD(glXSwapIntervalMESA); + GLXLOAD(glXGetSwapIntervalMESA); + + if (glXSwapIntervalMESA != 0){ + glXSwapIntervalMESA(1); + + if (glXGetSwapIntervalMESA != 0){ + linuxvars.vsync = glXGetSwapIntervalMESA(); + //LOGF("VSync enabled? %s (MESA)\n", linuxvars.vsync ? "Yes" : "No"); + } + else{ + // NOTE(inso): assume it worked? + linuxvars.vsync = true; + //LOG("VSync enabled? possibly (MESA)\n"); + } + } + + } + else if (Direct && strstr(glxExts, "GLX_SGI_swap_control ")){ + GLXLOAD(glXSwapIntervalSGI); + + if (glXSwapIntervalSGI){ + glXSwapIntervalSGI(1); + + // NOTE(inso): The SGI one doesn't seem to have a way to confirm we got it... + linuxvars.vsync = true; + //LOG("VSync enabled? hopefully (SGI)\n"); + } + + } + else{ + //LOG("VSync enabled? nope, no suitable extension\n"); + } + + return(ctx); +} + +internal b32 +GLXCanUseFBConfig(Display *XDisplay) +{ + b32 Result = false; + + int GLXMajor, GLXMinor; + + char *XVendor = ServerVendor(XDisplay); + //LOGF("XWindows vendor: %s\n", XVendor); + if (glXQueryVersion(XDisplay, &GLXMajor, &GLXMinor)) + { + //LOGF("GLX version %d.%d\n", GLXMajor, GLXMinor); + if (((GLXMajor == 1 ) && (GLXMinor >= 3)) || (GLXMajor > 1)) + { + Result = true; + } + } + + return(Result); +} + +struct glx_config_result{ + b32 Found; + GLXFBConfig BestConfig; + XVisualInfo BestInfo; +}; + +internal glx_config_result +ChooseGLXConfig(Display *XDisplay, int XScreenIndex) +{ + glx_config_result Result = {}; + + int DesiredAttributes[] = { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_ALPHA_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_STENCIL_SIZE , 8, + GLX_DOUBLEBUFFER , True, + None + }; + + int ConfigCount = 0; + GLXFBConfig *Configs = glXChooseFBConfig(XDisplay, XScreenIndex, DesiredAttributes, &ConfigCount); + if (Configs != 0 && ConfigCount > 0){ + XVisualInfo* VI = glXGetVisualFromFBConfig(XDisplay, Configs[0]); + if (VI != 0){ + Result.Found = true; + Result.BestConfig = Configs[0]; + Result.BestInfo = *VI; + + int id = 0; + glXGetFBConfigAttrib(XDisplay, Result.BestConfig, GLX_FBCONFIG_ID, &id); + //LOGF("Using FBConfig: %d (0x%x)\n", id, id); + + XFree(VI); + } + + XFree(Configs); + } + + return(Result); +} + +// +// X11 input / events init +// + +struct Init_Input_Result{ + XIM input_method; + XIMStyle best_style; + XIC xic; +}; +static Init_Input_Result null_init_input_result = {}; + +internal Init_Input_Result +LinuxInputInit(Display *dpy, Window XWindow){ + Init_Input_Result result = {}; + XIMStyles *styles = 0; + XIMStyle style; + unsigned long xim_event_mask = 0; + + setlocale(LC_ALL, ""); + XSetLocaleModifiers(""); + b32 locale_supported = XSupportsLocale(); + //LOGF("Supported locale?: %s.\n", locale_supported ? "Yes" : "No"); + if (!locale_supported){ + //LOG("Reverting to 'C' ... "); + setlocale(LC_ALL, "C"); + locale_supported = XSupportsLocale(); + //LOGF("C is supported? %s.\n", locale_supported ? "Yes" : "No"); + } + + result.input_method = XOpenIM(dpy, 0, 0, 0); + if (!result.input_method){ + // NOTE(inso): Try falling back to the internal XIM implementation that + // should in theory always exist. + + XSetLocaleModifiers("@im=none"); + result.input_method = XOpenIM(dpy, 0, 0, 0); + } + + if (result.input_method){ + if (!XGetIMValues(result.input_method, XNQueryInputStyle, &styles, NULL) && styles){ + for (i32 i = 0; i < styles->count_styles; ++i){ + style = styles->supported_styles[i]; + if (style == (XIMPreeditNothing | XIMStatusNothing)){ + result.best_style = style; + break; + } + } + } + + if (result.best_style){ + XFree(styles); + + result.xic = XCreateIC(result.input_method, XNInputStyle, result.best_style, XNClientWindow, XWindow, XNFocusWindow, XWindow, NULL); + + if (XGetICValues(result.xic, XNFilterEvents, &xim_event_mask, NULL)){ + xim_event_mask = 0; + } + } + else{ + result = null_init_input_result; + //LOG("Could not get minimum required input style.\n"); + exit(1); + } + } + else{ + result = null_init_input_result; + //LOG("Could not open X Input Method.\n"); + exit(1); + } + + u32 flags = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask | MappingNotify | ExposureMask | VisibilityChangeMask | xim_event_mask; + + XSelectInput(linuxvars.XDisplay, linuxvars.XWindow, flags); + + return(result); +} + +// +// Keyboard handling funcs +// + +#if 0 +internal void +LinuxKeycodeInit(Display* dpy){ + + // NOTE(inso): This looks a bit dumb, but it's the best way I can think of to do it, since: + // KeySyms are the type representing "virtual" keys, like XK_BackSpace, but they are 32-bit ints. + // KeyCodes are guaranteed to fit in 1 byte (and therefore the keycode_lookup_table) but + // have dynamic numbers assigned by the XServer. + // There is XKeysymToKeycode, but it only returns 1 KeyCode for a KeySym. I have my capslock + // rebound to esc, so there are two KeyCodes for the XK_Escape KeyCode but XKeysymToKeycode only + // gets one of them, hence the need for this crazy lookup which works correctly with rebound keys. + + memset(keycode_lookup_table, 0, sizeof(keycode_lookup_table)); + + struct SymMapping { + KeySym sym; + u16 code; + }; + + SymMapping sym_table[] = { + { XK_BackSpace, key_back }, + { XK_Delete, key_del }, + { XK_Up, key_up }, + { XK_Down, key_down }, + { XK_Left, key_left }, + { XK_Right, key_right }, + { XK_Insert, key_insert }, + { XK_Home, key_home }, + { XK_End, key_end }, + { XK_Page_Up, key_page_up }, + { XK_Page_Down, key_page_down }, + { XK_Escape, key_esc }, + { XK_F1, key_f1 }, + { XK_F2, key_f2 }, + { XK_F3, key_f3 }, + { XK_F4, key_f4 }, + { XK_F5, key_f5 }, + { XK_F6, key_f6 }, + { XK_F7, key_f7 }, + { XK_F8, key_f8 }, + { XK_F9, key_f9 }, + { XK_F10, key_f10 }, + { XK_F11, key_f11 }, + { XK_F12, key_f12 }, + { XK_F13, key_f13 }, + { XK_F14, key_f14 }, + { XK_F15, key_f15 }, + { XK_F16, key_f16 }, + }; + + const int table_size = sizeof(sym_table) / sizeof(struct SymMapping); + + int key_min, key_max, syms_per_code; + XDisplayKeycodes(dpy, &key_min, &key_max); + + int key_count = (key_max - key_min) + 1; + + KeySym* syms = XGetKeyboardMapping(dpy, key_min, key_count, &syms_per_code); + + if (syms == 0){ + return; + } + + int key = key_min; + for(int i = 0; i < key_count * syms_per_code; ++i){ + for(int j = 0; j < table_size; ++j){ + if (sym_table[j].sym == syms[i]){ + keycode_lookup_table[key + (i/syms_per_code)] = sym_table[j].code; + break; + } + } + } + + XFree(syms); +} +#endif + +internal void +LinuxPushKey(Key_Code code, Key_Code chr, Key_Code chr_nocaps, b8 *mods) +{ + i32 *count = &linuxvars.input.keys.count; + Key_Event_Data *data = linuxvars.input.keys.keys; + + if (*count < KEY_INPUT_BUFFER_SIZE){ + data[*count].keycode = code; + data[*count].character = chr; + data[*count].character_no_caps_lock = chr_nocaps; + + memcpy(data[*count].modifiers, mods, sizeof(*mods)*MDFR_INDEX_COUNT); + + ++(*count); + } +} + +// +// Misc utility funcs +// + +internal void +LinuxStringDup(String* str, void* data, size_t size){ + if (str->memory_size < size){ + if (str->str){ + system_memory_free(str->str, str->memory_size); + } + str->memory_size = size; + str->str = (char*)system_memory_allocate(size); + //TODO(inso): handle alloc failure case + } + + str->size = size; + memcpy(str->str, data, size); +} + +// +// X11 utility funcs +// + +internal void +LinuxX11ConnectionWatch(Display* dpy, XPointer cdata, int fd, Bool opening, XPointer* wdata){ + struct epoll_event e = {}; + e.events = EPOLLIN | EPOLLET; + e.data.u64 = LINUX_4ED_EVENT_X11_INTERNAL | fd; + + int op = opening ? EPOLL_CTL_ADD : EPOLL_CTL_DEL; + epoll_ctl(linuxvars.epoll, op, fd, &e); +} + +internal int +LinuxGetXSettingsDPI(Display* dpy, int screen) +{ + struct XSettingHeader { + u8 type; + u8 pad0; + u16 name_len; + char name[0]; + }; + + struct XSettings { + u8 byte_order; + u8 pad[3]; + u32 serial; + u32 num_settings; + }; + + enum { XSettingsTypeInt, XSettingsTypeString, XSettingsTypeColor }; + + int dpi = -1; + unsigned char* prop = NULL; + char sel_buffer[64]; + struct XSettings* xs; + const char* p; + + snprintf(sel_buffer, sizeof(sel_buffer), "_XSETTINGS_S%d", screen); + + Atom XSET_SEL = XInternAtom(dpy, sel_buffer, True); + Atom XSET_SET = XInternAtom(dpy, "_XSETTINGS_SETTINGS", True); + + if (XSET_SEL == None || XSET_SET == None){ + //LOG("XSETTINGS unavailable.\n"); + return(dpi); + } + + Window xset_win = XGetSelectionOwner(dpy, XSET_SEL); + if (xset_win == None){ + // TODO(inso): listen for the ClientMessage about it becoming available? + // there's not much point atm if DPI scaling is only done at startup + goto out; + } + + { + Atom type; + int fmt; + unsigned long pad, num; + + if (XGetWindowProperty(dpy, xset_win, XSET_SET, 0, 1024, False, XSET_SET, &type, &fmt, &num, &pad, &prop) != Success){ + //LOG("XSETTINGS: GetWindowProperty failed.\n"); + goto out; + } + + if (fmt != 8){ + //LOG("XSETTINGS: Wrong format.\n"); + goto out; + } + } + + xs = (struct XSettings*)prop; + p = (char*)(xs + 1); + + if (xs->byte_order != 0){ + //LOG("FIXME: XSETTINGS not host byte order?\n"); + goto out; + } + + for (int i = 0; i < xs->num_settings; ++i){ + struct XSettingHeader* h = (struct XSettingHeader*)p; + + p += sizeof(struct XSettingHeader); + p += h->name_len; + p += ((4 - (h->name_len & 3)) & 3); + p += 4; // serial + + switch (h->type){ + case XSettingsTypeInt: { + if (strncmp(h->name, "Xft/DPI", h->name_len) == 0){ + dpi = *(i32*)p; + if (dpi != -1) dpi /= 1024; + } + p += 4; + } break; + + case XSettingsTypeString: { + u32 len = *(u32*)p; + p += 4; + p += len; + p += ((4 - (len & 3)) & 3); + } break; + + case XSettingsTypeColor: { + p += 8; + } break; + + default: { + //LOG("XSETTINGS: Got invalid type...\n"); + goto out; + } break; + } + } + + out: + if (prop){ + XFree(prop); + } + + return dpi; +} + +// +// X11 window init +// + +internal f32 +size_change(i32 x, i32 y){ + f32 xs = x/96.f; + f32 ys = y/96.f; + f32 s = Min(xs, ys); + return(s); +} + +#define BASE_W 800 +#define BASE_H 600 + +internal b32 +LinuxX11WindowInit(int argc, char** argv, int* window_width, int* window_height){ + if (plat_settings.set_window_size){ + *window_width = plat_settings.window_w; + *window_height = plat_settings.window_h; + } else { + f32 schange = size_change(linuxvars.dpi_x, linuxvars.dpi_y); + *window_width = ceil32(BASE_W * schange); + *window_height = ceil32(BASE_H * schange); + } + *window_width = Max(*window_width, 1); + *window_height = Max(*window_height, 1); + + if (!GLXCanUseFBConfig(linuxvars.XDisplay)){ + system_error_box("Your XServer's GLX version is too old. GLX 1.3+ is required."); + } + + glx_config_result Config = ChooseGLXConfig(linuxvars.XDisplay, DefaultScreen(linuxvars.XDisplay)); + if (!Config.Found){ + system_error_box("Could not get a matching GLX FBConfig. Check your OpenGL drivers are installed correctly."); + } + + XSetWindowAttributes swa = {}; + swa.backing_store = WhenMapped; + swa.event_mask = StructureNotifyMask; + swa.bit_gravity = NorthWestGravity; + swa.colormap = XCreateColormap(linuxvars.XDisplay, RootWindow(linuxvars.XDisplay, Config.BestInfo.screen), Config.BestInfo.visual, AllocNone); + + u32 CWflags = CWBackingStore|CWBitGravity|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; + linuxvars.XWindow = XCreateWindow(linuxvars.XDisplay, RootWindow(linuxvars.XDisplay, Config.BestInfo.screen), 0, 0, *window_width, *window_height, 0, Config.BestInfo.depth, InputOutput, Config.BestInfo.visual, CWflags, &swa); + + if (!linuxvars.XWindow){ + system_error_box("XCreateWindow failed. Make sure your display is set up correctly."); + } + + //NOTE(inso): Set the window's type to normal + XChangeProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*)&linuxvars.atom__NET_WM_WINDOW_TYPE_NORMAL, 1); + + //NOTE(inso): window managers want the PID as a window property for some reason. + pid_t pid = getpid(); + XChangeProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom__NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&pid, 1); + + //NOTE(inso): set wm properties + XStoreName(linuxvars.XDisplay, linuxvars.XWindow, WINDOW_NAME); + + XSizeHints *sz_hints = XAllocSizeHints(); + XWMHints *wm_hints = XAllocWMHints(); + XClassHint *cl_hints = XAllocClassHint(); + + sz_hints->flags = PMinSize | PMaxSize | PWinGravity; + + sz_hints->min_width = 50; + sz_hints->min_height = 50; + + sz_hints->max_width = sz_hints->max_height = (1UL << 16UL); + + /* NOTE(inso): fluxbox thinks this is minimum, so don't set it + sz_hints->base_width = BASE_W; + sz_hints->base_height = BASE_H; + */ + sz_hints->win_gravity = NorthWestGravity; + + if (plat_settings.set_window_pos){ + sz_hints->flags |= USPosition; + sz_hints->x = plat_settings.window_x; + sz_hints->y = plat_settings.window_y; + } + + wm_hints->flags |= InputHint | StateHint; + wm_hints->input = True; + wm_hints->initial_state = NormalState; + + cl_hints->res_name = "4coder"; + cl_hints->res_class = "4coder"; + + char* win_name_list[] = { WINDOW_NAME }; + XTextProperty win_name; + XStringListToTextProperty(win_name_list, 1, &win_name); + + XSetWMProperties(linuxvars.XDisplay, linuxvars.XWindow, &win_name, NULL, argv, argc, sz_hints, wm_hints, cl_hints); + + XFree(win_name.value); + + XFree(sz_hints); + XFree(wm_hints); + XFree(cl_hints); + + linux_set_icon(linuxvars.XDisplay, linuxvars.XWindow); + + // NOTE(inso): make the window visible + XMapWindow(linuxvars.XDisplay, linuxvars.XWindow); + + GLXContext GLContext = + InitializeOpenGLContext(linuxvars.XDisplay, linuxvars.XWindow, &Config.BestConfig); + + XRaiseWindow(linuxvars.XDisplay, linuxvars.XWindow); + + if (plat_settings.set_window_pos){ + XMoveWindow(linuxvars.XDisplay, linuxvars.XWindow, plat_settings.window_x, plat_settings.window_y); + } + + if (plat_settings.maximize_window){ + linux_maximize_window(true); + } + else if (plat_settings.fullscreen_window){ + linux_toggle_fullscreen(); + } + + XSync(linuxvars.XDisplay, False); + + XWindowAttributes WinAttribs; + if (XGetWindowAttributes(linuxvars.XDisplay, linuxvars.XWindow, &WinAttribs)){ + *window_width = WinAttribs.width; + *window_height = WinAttribs.height; + } + + Atom wm_protos[] = { + linuxvars.atom_WM_DELETE_WINDOW, + linuxvars.atom__NET_WM_PING + }; + + XSetWMProtocols(linuxvars.XDisplay, linuxvars.XWindow, wm_protos, 2); + + return(true); +} + +internal void +linux_mod_flags_to_array(u32 state, b8 *mods){ + mods[MDFR_SHIFT_INDEX] = ((state & ShiftMask) != 0); + mods[MDFR_CONTROL_INDEX] = ((state & ControlMask) != 0); + mods[MDFR_CAPS_INDEX] = ((state & LockMask) != 0); + mods[MDFR_ALT_INDEX] = ((state & Mod1Mask) != 0); +} + +internal void +linux_handle_x11_events(void) +{ + static XEvent prev_event = {}; + b32 should_step = false; + + while (XPending(linuxvars.XDisplay)) + { + XEvent event; + XNextEvent(linuxvars.XDisplay, &event); + + if (XFilterEvent(&event, None) == True){ + continue; + } + + switch (event.type){ + case KeyPress: + { + should_step = true; + + b32 is_hold = (prev_event.type == KeyRelease && + prev_event.xkey.time == event.xkey.time && + prev_event.xkey.keycode == event.xkey.keycode); + + b8 mods[MDFR_INDEX_COUNT] = {}; + linux_mod_flags_to_array(event.xkey.state, mods); + linux_mod_flags_to_array(event.xkey.state, linuxvars.modifiers); + mods[MDFR_HOLD_INDEX] = is_hold; + + event.xkey.state &= ~(ControlMask); + + Status status; + KeySym keysym = NoSymbol; + u8 buff[32] = {}; + + Xutf8LookupString(linuxvars.input_context, &event.xkey, (char*)buff, sizeof(buff) - 1, &keysym, &status); + + if (status == XBufferOverflow){ + //TODO(inso): handle properly + Xutf8ResetIC(linuxvars.input_context); + XSetICFocus(linuxvars.input_context); + //LOG("FIXME: XBufferOverflow from LookupString.\n"); + } + + // don't push modifiers + if (keysym >= XK_Shift_L && keysym <= XK_Hyper_R){ + break; + } + + u32 key = utf8_to_u32_unchecked(buff); + u32 key_no_caps = key; + + if (mods[MDFR_CAPS_INDEX] && status == XLookupBoth && event.xkey.keycode){ + u8 buff_no_caps[32] = {}; + event.xkey.state &= ~(LockMask); + + Xutf8LookupString(linuxvars.input_context, &event.xkey, (char*)buff_no_caps, sizeof(buff_no_caps) - 1, NULL, &status); + + if (status == XBufferOverflow){ + //TODO(inso): handle properly + Xutf8ResetIC(linuxvars.input_context); + XSetICFocus(linuxvars.input_context); + //LOG("FIXME: XBufferOverflow from LookupString.\n"); + } + + if (*buff_no_caps){ + key_no_caps = utf8_to_u32_unchecked(buff_no_caps); + } + } + + if (key == '\r') key = '\n'; + if (key_no_caps == '\r') key_no_caps = '\n'; + + if (keysym == XK_ISO_Left_Tab){ + key = key_no_caps = '\t'; + mods[MDFR_SHIFT_INDEX] = 1; + } + + //Key_Code special_key = keycode_lookup_table[keysym]; + + Key_Code special_key = 0; + switch (keysym){ + case XK_BackSpace: + { + special_key = key_back; + }break; + + case XK_Delete: + { + special_key = key_del; + }break; + + case XK_Up: + { + special_key = key_up; + }break; + + case XK_Down: + { + special_key = key_down; + }break; + + case XK_Left: + { + special_key = key_left; + }break; + + case XK_Right: + { + special_key = key_right; + }break; + + case XK_Insert: + { + special_key = key_insert; + }break; + + case XK_Home: + { + special_key = key_home; + }break; + + case XK_End: + { + special_key = key_end; + }break; + + case XK_Page_Up: + { + special_key = key_page_up; + }break; + + case XK_Page_Down: + { + special_key = key_page_down; + }break; + + case XK_Escape: + { + special_key = key_esc; + }break; + + case XK_F1: + { + special_key = key_f1; + }break; + + case XK_F2: + { + special_key = key_f2; + }break; + + case XK_F3: + { + special_key = key_f3; + }break; + + case XK_F4: + { + special_key = key_f4; + }break; + + case XK_F5: + { + special_key = key_f5; + }break; + + case XK_F6: + { + special_key = key_f6; + }break; + + case XK_F7: + { + special_key = key_f7; + }break; + + case XK_F8: + { + special_key = key_f8; + }break; + + case XK_F9: + { + special_key = key_f9; + }break; + + case XK_F10: + { + special_key = key_f10; + }break; + + case XK_F11: + { + special_key = key_f11; + }break; + + case XK_F12: + { + special_key = key_f12; + }break; + + case XK_F13: + { + special_key = key_f13; + }break; + + case XK_F14: + { + special_key = key_f14; + }break; + + case XK_F15: + { + special_key = key_f15; + }break; + + case XK_F16: + { + special_key = key_f16; + }break; + } + + + if (special_key){ + LinuxPushKey(special_key, 0, 0, mods); + } + else if (key != 0){ + LinuxPushKey(key, key, key_no_caps, mods); + } + else{ + LinuxPushKey(0, 0, 0, mods); + } + }break; + + case KeyRelease: + { + should_step = true; + }break; + + case MotionNotify: + { + should_step = true; + linuxvars.input.mouse.x = event.xmotion.x; + linuxvars.input.mouse.y = event.xmotion.y; + linux_mod_flags_to_array(event.xmotion.state, linuxvars.modifiers); + }break; + + case ButtonPress: + { + should_step = true; + linux_mod_flags_to_array(event.xbutton.state, linuxvars.modifiers); + switch (event.xbutton.button){ + case Button1: + { + linuxvars.input.mouse.press_l = true; + linuxvars.input.mouse.l = true; + } break; + case Button3: + { + linuxvars.input.mouse.press_r = true; + linuxvars.input.mouse.r = true; + } break; + + // NOTE(inso): scroll up + case Button4: + { + linuxvars.input.mouse.wheel = -100; + }break; + + // NOTE(inso): scroll down + case Button5: + { + linuxvars.input.mouse.wheel = 100; + }break; + } + }break; + + case ButtonRelease: + { + should_step = true; + linux_mod_flags_to_array(event.xbutton.state, linuxvars.modifiers); + switch(event.xbutton.button){ + case Button1: + { + linuxvars.input.mouse.release_l = 1; + linuxvars.input.mouse.l = 0; + }break; + case Button3: + { + linuxvars.input.mouse.release_r = 1; + linuxvars.input.mouse.r = 0; + }break; + } + }break; + + case EnterNotify: + { + should_step = true; + linuxvars.input.mouse.out_of_window = 0; + }break; + + case LeaveNotify: + { + should_step = true; + linuxvars.input.mouse.out_of_window = 1; + }break; + + case FocusIn: + case FocusOut: + { + should_step = true; + linuxvars.input.mouse.l = 0; + linuxvars.input.mouse.r = 0; + }break; + + case ConfigureNotify: + { + should_step = true; + i32 w = event.xconfigure.width; + i32 h = event.xconfigure.height; + + if (w != target.width || h != target.height){ + LinuxResizeTarget(w, h); + } + }break; + + case MappingNotify: + { + if (event.xmapping.request == MappingModifier || event.xmapping.request == MappingKeyboard){ + XRefreshKeyboardMapping(&event.xmapping); + //LinuxKeycodeInit(linuxvars.XDisplay); + } + }break; + + case ClientMessage: + { + if ((Atom)event.xclient.data.l[0] == linuxvars.atom_WM_DELETE_WINDOW){ + should_step = true; + linuxvars.keep_running = 0; + } + else if ((Atom)event.xclient.data.l[0] == linuxvars.atom__NET_WM_PING){ + event.xclient.window = DefaultRootWindow(linuxvars.XDisplay); + XSendEvent( + linuxvars.XDisplay, + event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &event); + } + }break; + + // NOTE(inso): Someone wants us to give them the clipboard data. + case SelectionRequest: { + XSelectionRequestEvent request = event.xselectionrequest; + + XSelectionEvent response = {}; + response.type = SelectionNotify; + response.requestor = request.requestor; + response.selection = request.selection; + response.target = request.target; + response.time = request.time; + response.property = None; + + if (linuxvars.clipboard_outgoing.size && + request.selection == linuxvars.atom_CLIPBOARD && + request.property != None && + request.display && + request.requestor){ + Atom atoms[] = { + XA_STRING, + linuxvars.atom_UTF8_STRING + }; + + if (request.target == linuxvars.atom_TARGETS){ + XChangeProperty( + request.display, + request.requestor, + request.property, + XA_ATOM, + 32, + PropModeReplace, + (u8*)atoms, + ArrayCount(atoms)); + + response.property = request.property; + + } else { + b32 found = false; + for(int i = 0; i < ArrayCount(atoms); ++i){ + if (request.target == atoms[i]){ + found = true; + break; + } + } + + if (found){ + XChangeProperty( + request.display, + request.requestor, + request.property, + request.target, + 8, + PropModeReplace, + (u8*)linuxvars.clipboard_outgoing.str, + linuxvars.clipboard_outgoing.size + ); + + response.property = request.property; + } + } + } + + XSendEvent(request.display, request.requestor, True, 0, (XEvent*)&response); + + }break; + + // NOTE(inso): Another program is now the clipboard owner. + case SelectionClear: { + if (event.xselectionclear.selection == linuxvars.atom_CLIPBOARD){ + linuxvars.clipboard_outgoing.size = 0; + } + }break; + + // NOTE(inso): A program is giving us the clipboard data we asked for. + case SelectionNotify: { + XSelectionEvent* e = (XSelectionEvent*)&event; + if (e->selection == linuxvars.atom_CLIPBOARD && e->target == linuxvars.atom_UTF8_STRING && e->property != None){ + Atom type; + int fmt; + unsigned long nitems; + unsigned long bytes_left; + u8 *data; + + +#define PASTE_MAX_WORDS 0x20000000L + int result = XGetWindowProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD, 0L, PASTE_MAX_WORDS, False, linuxvars.atom_UTF8_STRING, &type, &fmt, &nitems, &bytes_left, &data); + + if (result == Success && fmt == 8){ + LinuxStringDup(&linuxvars.clipboard_contents, data, nitems); + should_step = true; + linuxvars.new_clipboard = true; + XFree(data); + XDeleteProperty(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD); + } + } + }break; + + case Expose: + case VisibilityNotify: { + should_step = true; + }break; + + default: + { + if (event.type == linuxvars.xfixes_selection_event){ + XFixesSelectionNotifyEvent* sne = (XFixesSelectionNotifyEvent*)&event; + if (sne->subtype == XFixesSelectionNotify && sne->owner != linuxvars.XWindow){ + XConvertSelection(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.atom_UTF8_STRING, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); + } + } + }break; + } + + prev_event = event; + } + + if (should_step){ + system_schedule_step(); + } +} + +#include "4ed_link_system_functions.cpp" +#include "4ed_shared_init_logic.cpp" + +int +main(int argc, char **argv){ + // + // System Linkage + // + + link_system_code(); + + // + // Memory init + // + + memset(&linuxvars, 0, sizeof(linuxvars)); + memset(&target, 0, sizeof(target)); + memset(&memory_vars, 0, sizeof(memory_vars)); + memset(&plat_settings, 0, sizeof(plat_settings)); + + memset(&libraries, 0, sizeof(libraries)); + memset(&app, 0, sizeof(app)); + memset(&custom_api, 0, sizeof(custom_api)); + + memory_init(); + + // + // HACK(allen): + // Previously zipped stuff is here, it should be zipped in the new pattern now. + // + + init_shared_vars(); + + linuxvars.track_table_size = KB(16); + linuxvars.track_table = system_memory_allocate(linuxvars.track_table_size); + + linuxvars.track_node_size = KB(16); + void *track_nodes = system_memory_allocate(linuxvars.track_node_size); + + i32 track_result = init_track_system(&linuxvars.track, &shared_vars.scratch, + linuxvars.track_table, linuxvars.track_table_size, + track_nodes, linuxvars.track_node_size); + + if (track_result != FileTrack_Good){ + exit(1); + } + + // + // Load Core Code + // + load_app_code(); + + // + // Read command line + // + read_command_line(argc, argv); + + // + // Load Custom Code + // +#if defined(FRED_SUPER) + load_custom_code(); +#else + DontCompile; +#endif + + // + // Threads + // + + work_system_init(); + + // + // Coroutines + // + + coroutines_init(); + + // + // X11 init + // + + linuxvars.XDisplay = XOpenDisplay(0); + if (!linuxvars.XDisplay){ + // NOTE(inso): probably not worth trying the popup in this case... + //LOG("Can't open display!\n"); + exit(1); + } + +#define LOAD_ATOM(x) linuxvars.atom_##x = XInternAtom(linuxvars.XDisplay, #x, False); + + LOAD_ATOM(TARGETS); + LOAD_ATOM(CLIPBOARD); + LOAD_ATOM(UTF8_STRING); + LOAD_ATOM(_NET_WM_STATE); + LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); + LOAD_ATOM(_NET_WM_PING); + LOAD_ATOM(_NET_WM_WINDOW_TYPE); + LOAD_ATOM(_NET_WM_WINDOW_TYPE_NORMAL); + LOAD_ATOM(_NET_WM_PID); + LOAD_ATOM(WM_DELETE_WINDOW); + +#undef LOAD_ATOM + + linuxvars.dpi_x = linuxvars.dpi_y = LinuxGetXSettingsDPI(linuxvars.XDisplay, DefaultScreen(linuxvars.XDisplay)); + + // fallback + if (linuxvars.dpi_x == -1){ + int scr = DefaultScreen(linuxvars.XDisplay); + + int dw = DisplayWidth(linuxvars.XDisplay, scr); + int dh = DisplayHeight(linuxvars.XDisplay, scr); + + int dw_mm = DisplayWidthMM(linuxvars.XDisplay, scr); + int dh_mm = DisplayHeightMM(linuxvars.XDisplay, scr); + + linuxvars.dpi_x = dw_mm ? dw / (dw_mm / 25.4) : 96; + linuxvars.dpi_y = dh_mm ? dh / (dh_mm / 25.4) : 96; + + //LOGF("%dx%d - %dmmx%dmm DPI: %dx%d\n", dw, dh, dw_mm, dh_mm, linuxvars.dpi_x, linuxvars.dpi_y); + } + else{ + //LOGF("DPI from XSETTINGS: %d\n", linuxvars.dpi_x); + } + + int window_width, window_height; + if (!LinuxX11WindowInit(argc, argv, &window_width, &window_height)){ + return 1; + } + + int xfixes_version_unused, xfixes_err_unused; + b32 xquery_extension_r = XQueryExtension(linuxvars.XDisplay, "XFIXES", &xfixes_version_unused, &linuxvars.xfixes_selection_event, &xfixes_err_unused); + linuxvars.has_xfixes = (xquery_extension_r == True); + + if (linuxvars.has_xfixes){ + XFixesSelectSelectionInput(linuxvars.XDisplay, linuxvars.XWindow, linuxvars.atom_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask); + } + else{ + //LOG("Your X server doesn't support XFIXES, mention this fact if you report any clipboard-related issues.\n"); + } + + Init_Input_Result input_result = LinuxInputInit(linuxvars.XDisplay, linuxvars.XWindow); + + linuxvars.input_method = input_result.input_method; + linuxvars.input_style = input_result.best_style; + linuxvars.input_context = input_result.xic; + + //LinuxKeycodeInit(linuxvars.XDisplay); + + Cursor xcursors[APP_MOUSE_CURSOR_COUNT] = { + None, + None, + XCreateFontCursor(linuxvars.XDisplay, XC_xterm), + XCreateFontCursor(linuxvars.XDisplay, XC_sb_h_double_arrow), + XCreateFontCursor(linuxvars.XDisplay, XC_sb_v_double_arrow) + }; + + { + char data = 0; + XColor c = {}; + Pixmap p = XCreateBitmapFromData(linuxvars.XDisplay, linuxvars.XWindow, &data, 1, 1); + + linuxvars.hidden_cursor = XCreatePixmapCursor(linuxvars.XDisplay, p, p, &c, &c, 0, 0); + + XFreePixmap(linuxvars.XDisplay, p); + } + + + // + // Font System Init + // + + Partition *scratch = &shared_vars.scratch; + Temp_Memory temp = begin_temp_memory(scratch); + Font_Setup_List font_setup = system_font_get_local_stubs(scratch); + linux_get_loadable_fonts(scratch, &font_setup); + system_font_init(&sysfunc.font, plat_settings.font_size, plat_settings.use_hinting, font_setup); + end_temp_memory(temp); + + // + // Epoll init + // + + linuxvars.x11_fd = ConnectionNumber(linuxvars.XDisplay); + linuxvars.inotify_fd = inotify_init1(IN_NONBLOCK); + linuxvars.step_event_fd = eventfd(0, EFD_NONBLOCK); + linuxvars.step_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + + linuxvars.epoll = epoll_create(16); + + { + struct epoll_event e = {}; + e.events = EPOLLIN | EPOLLET; + + e.data.u64 = LINUX_4ED_EVENT_X11; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.x11_fd, &e); + + e.data.u64 = LINUX_4ED_EVENT_STEP; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_event_fd, &e); + + e.data.u64 = LINUX_4ED_EVENT_STEP_TIMER; + epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_timer_fd, &e); + } + + // + // App init + // + + XAddConnectionWatch(linuxvars.XDisplay, &LinuxX11ConnectionWatch, NULL); + + char cwd[4096]; + u32 size = sysfunc.get_current_path(cwd, sizeof(cwd)); + if (size == 0 || size >= sizeof(cwd)){ + system_error_box("Could not get current directory at launch."); + } + String curdir = make_string(cwd, size); + terminate_with_null(&curdir); + replace_char(&curdir, '\\', '/'); + + //LOG("Initializing application variables\n"); + app.init(&sysfunc, &target, &memory_vars, linuxvars.clipboard_contents, curdir, custom_api); + + LinuxResizeTarget(window_width, window_height); + + // + // Main loop + // + + system_acquire_lock(FRAME_LOCK); + + system_schedule_step(); + + linuxvars.keep_running = true; + linuxvars.input.first_step = true; + linuxvars.input.dt = (frame_useconds / 1000000.f); + + for (;;){ + if (XEventsQueued(linuxvars.XDisplay, QueuedAlready)){ + linux_handle_x11_events(); + } + + system_release_lock(FRAME_LOCK); + + struct epoll_event events[16]; + int num_events = epoll_wait(linuxvars.epoll, events, ArrayCount(events), -1); + + system_acquire_lock(FRAME_LOCK); + + if (num_events == -1){ + if (errno != EINTR){ + //LOG("epoll_wait\n"); + } + continue; + } + + b32 do_step = false; + + for (int i = 0; i < num_events; ++i){ + int fd = events[i].data.u64 & UINT32_MAX; + u64 type = events[i].data.u64 & ~fd; + + switch (type){ + case LINUX_4ED_EVENT_X11: + { + linux_handle_x11_events(); + } break; + + case LINUX_4ED_EVENT_X11_INTERNAL: { + XProcessInternalConnection(linuxvars.XDisplay, fd); + } break; + + case LINUX_4ED_EVENT_STEP: { + u64 ev; + int ret; + do { + ret = read(linuxvars.step_event_fd, &ev, 8); + } while (ret != -1 || errno != EAGAIN); + do_step = true; + } break; + + case LINUX_4ED_EVENT_STEP_TIMER: { + u64 count; + int ret; + do { + ret = read(linuxvars.step_timer_fd, &count, 8); + } while (ret != -1 || errno != EAGAIN); + do_step = true; + } break; + + case LINUX_4ED_EVENT_CLI: { + system_schedule_step(); + } break; + } + } + + if (do_step){ + linuxvars.last_step = system_now_time(); + + // NOTE(allen): Frame Clipboard Input + if (linuxvars.input.first_step || !linuxvars.has_xfixes){ + XConvertSelection(linuxvars.XDisplay, linuxvars.atom_CLIPBOARD, linuxvars.atom_UTF8_STRING, linuxvars.atom_CLIPBOARD, linuxvars.XWindow, CurrentTime); + } + + if (linuxvars.new_clipboard){ + linuxvars.input.clipboard = linuxvars.clipboard_contents; + linuxvars.new_clipboard = 0; + } + else{ + linuxvars.input.clipboard = null_string; + } + + Application_Step_Input frame_input = linuxvars.input; + frame_input.trying_to_kill = !linuxvars.keep_running; + memcpy(frame_input.keys.modifiers, linuxvars.modifiers, sizeof(frame_input.keys.modifiers)); + + // HACK(allen): THIS SHIT IS FUCKED (happens on mac too) + b32 keep_running = linuxvars.keep_running; + + // NOTE(allen): Application Core Update + target.buffer.pos = 0; + Application_Step_Result result = {}; + if (app.step != 0){ + result = app.step(&sysfunc, &target, &memory_vars, &frame_input); + } + else{ + //LOG("app.step == 0 -- skipping\n"); + } + + + // NOTE(allen): Finish the Loop + if (result.perform_kill){ + break; + } + else if (!keep_running && !linuxvars.keep_running){ + linuxvars.keep_running = true; + } + + // NOTE(NAME): Switch to New Title + if (result.has_new_title){ + XStoreName(linuxvars.XDisplay, linuxvars.XWindow, result.title_string); + } + + // NOTE(allen): Switch to New Cursor + if (result.mouse_cursor_type != linuxvars.cursor && !linuxvars.input.mouse.l){ + Cursor c = xcursors[result.mouse_cursor_type]; + if (!linuxvars.hide_cursor){ + XDefineCursor(linuxvars.XDisplay, linuxvars.XWindow, c); + } + linuxvars.cursor = result.mouse_cursor_type; + } + + // NOTE(allen): Render + gl_render(&target, &shared_vars.pixel_scratch); + glXSwapBuffers(linuxvars.XDisplay, linuxvars.XWindow); + + // NOTE(allen): Toggle Full Screen + if (linuxvars.do_toggle){ + linux_toggle_fullscreen(); + linuxvars.do_toggle = false; + } + + // NOTE(allen): Schedule Another Step if Needed + if (result.animating){ + system_schedule_step(); + } + + flush_thread_group(BACKGROUND_THREADS); + + // TODO(allen): CROSS REFERENCE WITH WIN32 "TIC898989" + memset(&linuxvars.input.keys, 0, sizeof(linuxvars.input.keys)); + linuxvars.input.mouse.press_l = 0; + linuxvars.input.mouse.release_l = 0; + linuxvars.input.mouse.press_r = 0; + linuxvars.input.mouse.release_r = 0; + linuxvars.input.mouse.wheel = 0; + + linuxvars.input.first_step = 0; + } + } + + if (linuxvars.XDisplay){ + if (linuxvars.XWindow){ + XDestroyWindow(linuxvars.XDisplay, linuxvars.XWindow); + } + XCloseDisplay(linuxvars.XDisplay); + } + + return(0); +} + +#include "linux_4ed_file_track.cpp" + +// BOTTOM +// vim: expandtab:ts=4:sts=4:sw=4 +