From 1375b100214f4e2163edef48799ff8b0a3d36106 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Luk=C3=A1=C5=A1=20Ji=C5=99i=C5=A1t=C4=9B?= Date: Fri, 12 Apr 2024 17:38:17 +0200 Subject: [PATCH] Implement foundation for windows It may be buggy and empty for now, but it formulates the basic idea. Many functions go unused now because I wanted to have this commit as small as possible (it is quite large as is). Some are defined and not used, some had to be commented out to make the project pass compilation. Immediate future work should either fix bugs or implement some visual output to better detect bugs to be fixed. --- Makefile | 14 +- TODO | 3 + inc/FET_sim.h | 80 ++++++++-- src/c_setnode.c | 2 +- src/main.c | 405 ++++++++++++++++++++++++++++++++++++++++-------- src/terminal.c | 37 ++++- src/text.c | 52 ++++--- 7 files changed, 477 insertions(+), 116 deletions(-) diff --git a/Makefile b/Makefile index 1e65f98..e4faf39 100644 --- a/Makefile +++ b/Makefile @@ -28,13 +28,13 @@ SOURCES := main.c \ \ build_helper.c \ \ - c_addnode.c \ - c_addfet.c \ - c_setnode.c \ - c_bind.c \ - c_draw.c \ - c_next.c \ - c_help.c \ +# c_addnode.c \ +# c_addfet.c \ +# c_setnode.c \ +# c_bind.c \ +# c_draw.c \ +# c_next.c \ +# c_help.c \ SOURCES := $(addprefix $(SRCDIR)/, $(SOURCES)) diff --git a/TODO b/TODO index 4a9f33b..b813ef0 100644 --- a/TODO +++ b/TODO @@ -4,6 +4,7 @@ implement vim-esque modes and gdb-like split screen/window for commands and sche handle the behaviour better sync a char *str_inp to what the user sees in terminal implement "split screen" - one for commands, one for schematics (gdb like) + fix crash when scrolling left or right commands screen should work just similarly to normal terminal usage schematics should use vim like controls each is controllable from a "mode" (at least 2 modes necessary, more may be usefull later on) @@ -28,6 +29,8 @@ implement vim-esque modes and gdb-like split screen/window for commands and sche use alternate screen implement non-canonical mode return input + implement "split screen" - one for commands, one for schematics (gdb like) + program the foundations for windows rewrite t_fet and t_node to store indexes insted of pointers to connected parts - goal shifted a bit, nodes store their ID and FETs have no use for havind ID so far foundations of logic diff --git a/inc/FET_sim.h b/inc/FET_sim.h index 263197d..f639c6c 100644 --- a/inc/FET_sim.h +++ b/inc/FET_sim.h @@ -49,14 +49,6 @@ typedef struct s_mosfet t_node *drain; } t_mosfet; -typedef enum e_mode -{ - mode_schema, - mode_command, - mode_exit, - mode_error, -} t_mode; - typedef enum e_command { none, @@ -101,6 +93,65 @@ typedef struct s_input t_arg argv[MAX_ARGS]; } t_input; +typedef enum e_mode +{ + mode_schema, + mode_command, + mode_exit, + mode_error, +} t_mode; + +// I don't want to name it position as I use it for size as well +// I also don't want to name it vector, as I use the t_vec struct +// So I use the name pair, which may be a little too general +// but is the best I can come with for now +// +// I use the top left corner of rectangles as their position +// The coordinate system I use looks like this: +// +// 0--------> x +// | +// | +// | +// | +// V +// +// y +typedef struct s_pair +{ + size_t x; + size_t y; +} t_pair; + +typedef struct s_window +{ + t_mode type; + t_pair pos; + t_pair size; + t_pair cursor; + t_pair pos_in_content; + t_mat content; + t_vec situational; +} t_window; + +typedef struct s_tui +{ + t_pair terminal_size; + t_window *active_win; + t_vec windows; +} t_tui; + +# define MAIN_COMMAND_W 0 +# define MAIN_SCHEMA_W 1 + +# define COMMAND_WINDOW_DEFAULT_HEIGHT 5 + +# define SYMBOL_SIZE 4 + +typedef struct s_symbol +{ + char sym[SYMBOL_SIZE]; +} t_symbol; void add_node(t_vec *nodes, t_state set_state); void add_mosfet(t_vec *mosfets, t_type type); @@ -119,16 +170,20 @@ int sim_step(t_vec *nodes, t_vec *mosfets); void setup_terminal(void); void clean_terminal(void); -void print_start(void); -void command_not_found(const char *input); -void print_help(t_command c); -void print_index_error(size_t index, size_t size); +t_pair get_terminal_size(void); +void print_start(t_window *win); +void command_not_found(t_window *win, const char *input); +void print_help(t_window *win, t_command c); +void print_index_error(t_window *win, size_t index, size_t size); + +void print_to_win(t_window *win, const char *str); t_state simple_resolve_state(t_state s1, t_state s2); t_state resolve_state(t_vec *connected_nodes); void apply_state(t_state state, t_vec *connected_nodes); t_state reduce_state(t_state state); +/* int c_addfet(t_input input, t_vec *mosfets); int c_addnode(t_input input, t_vec *nodes); int c_bind(t_input input, t_vec *nodes, t_vec *mosfets); @@ -136,4 +191,5 @@ int c_draw(t_input input, t_vec *mosfets); int c_help(t_input input); int c_next(t_input input, t_vec *nodes, t_vec *mosfets); int c_setnode(t_input input, t_vec *nodes); +*/ #endif //FET_SIM_H diff --git a/src/c_setnode.c b/src/c_setnode.c index ab849b0..3f18be6 100644 --- a/src/c_setnode.c +++ b/src/c_setnode.c @@ -13,7 +13,7 @@ int c_setnode(t_input input, t_vec *nodes) node = ft_vec_access(nodes, input.argv[0].val.num); if (!node) { - print_index_error(input.argv[0].val.num, nodes->size); + print_index_error(win, input.argv[0].val.num, nodes->size); return (1); } node->set_state = input.argv[1].val.state; diff --git a/src/main.c b/src/main.c index 746a3d2..f366201 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,15 @@ #include #include +#define BUFFER_SIZE 10 + +void free_node(void *node) +{ + ft_vec_free(&((t_node *)node)->connected, NULL); + return ; +} + +/* void build_graph(const char *filename, t_vec *nodes, t_vec *mosfets) { int fd; @@ -21,12 +30,6 @@ void build_graph(const char *filename, t_vec *nodes, t_vec *mosfets) return ; } -void free_node(void *node) -{ - ft_vec_free(&((t_node *)node)->connected, NULL); - return ; -} - void free_split(char **sp) { size_t i; @@ -232,74 +235,345 @@ t_mode process_input_string(t_input *input, char *str_inp) free_split(split_inp); return (mode_command); } +*/ -int handle_escape_code(__attribute__((unused)) t_input *input, __attribute__((unused)) const char *code) +t_window *process_input_command(__attribute__((unused)) const char *buffer, t_tui *tui, __attribute__((unused)) t_vec *nodes, __attribute__((unused)) t_vec *mosfets) { - return (1); + return (tui->active_win); +} + +int init_win(t_window *win, t_mode type, t_pair pos, t_pair size) +{ + static t_symbol filler = {.sym = " "}; + size_t i; + + win->type = type; + win->pos = pos; + win->size = size; + win->pos_in_content = (t_pair){.x = 0, .y = 0}; + ft_mat_init(&win->content, sizeof(t_symbol)); + ft_vec_init(&win->situational, sizeof(char)); + if (ft_mat_zeros(&win->content, size.y - 1, size.x) != success) + return (1); + ft_mat_fill(&win->content, &filler); + if (type == mode_schema) + { + win->cursor = (t_pair){.x = size.x / 2, .y = size.y / 2}; + if (ft_vec_reserve(&win->situational, size.x) != success) + { + ft_mat_free(&win->content, NULL); + return (1); + } + i = 0; + while (i < size.x) + { + ft_vec_append(&win->situational, " "); + ++i; + } + } + else + win->cursor = (t_pair){.x = 0, .y = 0}; + return (0); +} + +void destroy_win(void *window) +{ + t_window *win; + + win = window; + ft_mat_free(&win->content, NULL); + ft_vec_free(&win->situational, NULL); + return ; } -t_mode get_input(t_input *input, int fd) +// Let's create a convention that the 0th window is the main command one +// and that the 1st window is the main schema one +// This is reflected in FET_sim.h as macro definitions +// MAIN_COMMAND_W and MAIN_SCHEMA_W +int init_tui(t_tui *tui) { - int res; - static char *str_inp; - char buff[10]; - size_t len; + t_window tmp; + t_pair term; + size_t command_height; - while(1) + term = get_terminal_size(); + tui->terminal_size = term; + ft_vec_init(&tui->windows, sizeof(t_window)); + command_height = ft_mins(term.y, COMMAND_WINDOW_DEFAULT_HEIGHT); + if (init_win(&tmp, mode_command, + (t_pair){.x = 0, .y = term.y - command_height}, + (t_pair){.x = term.x, .y = command_height})) + return (1); + if (ft_vec_append(&tui->windows, &tmp)) + { + destroy_win(&tmp); + return (1); + } + if (init_win(&tmp, mode_schema, + (t_pair){.x = 0, .y = 0}, + (t_pair){.x = term.x, .y = term.y - command_height})) + { + destroy_win(ft_vec_access(&tui->windows, 0)); + return (1); + } + if (ft_vec_append(&tui->windows, &tmp)) + { + destroy_win(ft_vec_access(&tui->windows, 0)); + destroy_win(&tmp); + return (1); + } + tui->active_win = ft_vec_access(&tui->windows, 1); + return (0); +} + +void move_cursor(t_window *win, int dx, int dy) +{ + static const t_symbol empty_sym = {.sym = " "}; + t_vec empty_vec; + + ft_vec_init(&empty_vec, sizeof(t_symbol)); + ft_vec_append_range(&empty_vec, &empty_sym, win->content.rows); + if (!((dx < 0 && (size_t)-dx > win->cursor.x) + || dx + win->cursor.x >= win->size.x)) + win->cursor.x += dx; + else if (dx < 0) + { + dx += (int)win->cursor.x; + win->cursor.x = 0; + if ((size_t)-dx <= win->pos_in_content.x) + win->pos_in_content.x += dx; + else + { + dx += (int)win->pos_in_content.x; + win->pos_in_content.x = 0; + while (dx != 0) + { + ft_mat_insert_col(&win->content, &empty_vec, 0); + ++dx; + } + } + } + else { - len = read(fd, buff, 10); - buff[len] = '\0'; - if (buff[0] == '\033' && len == 1) - return (mode_schema); - else if (buff[0] == '\033') + dx -= win->size.x - win->cursor.x - 1; + win->cursor.x = win->size.x - 1; + if (win->pos_in_content.x + win->size.x - 1 + dx < win->content.cols) + win->pos_in_content.x += dx; + else { - if (handle_escape_code(input, buff)) - ft_printf("%s", buff); + dx -= win->content.cols - win->pos_in_content.x - win->size.x; + win->pos_in_content.x = win->content.cols - win->size.x; + while (dx != 0) + { + ft_mat_append_col(&win->content, &empty_vec); + ++win->pos_in_content.x; + --dx; + } } + } + if (empty_vec.size > win->content.rows) + ft_vec_forget_range(&empty_vec, win->content.rows, empty_vec.size - win->content.rows); + else + ft_vec_append_range(&empty_vec, &empty_sym, win->content.rows - empty_vec.size); + if (!((dy < 0 && (size_t)-dy > win->cursor.y) + || dy + win->cursor.y >= win->size.y)) + win->cursor.y += dy; + else if (dy < 0) + { + dy += (int)win->cursor.y; + win->cursor.y = 0; + if ((size_t)-dy <= win->pos_in_content.y) + win->pos_in_content.y += dy; else - ft_printf("%s", buff); + { + dy += (int)win->pos_in_content.y; + win->pos_in_content.y = 0; + while (dy != 0) + { + ft_mat_insert_row(&win->content, &empty_vec, 0); + ++dy; + } + } + } + else + { + dy -= win->size.y - win->cursor.y - 1; + win->cursor.y = win->size.y - 1; + if (win->pos_in_content.y + win->size.y - 1 + dy < win->content.cols) + win->pos_in_content.y += dy; + else + { + dy -= win->content.cols - win->pos_in_content.y - win->size.y; + win->pos_in_content.y = win->content.cols - win->size.y; + while (dy != 0) + { + ft_mat_append_row(&win->content, &empty_vec); + ++win->pos_in_content.y; + --dy; + } + } + } + ft_vec_free(&empty_vec, NULL); + ft_printf("\033[%i;%iH", 1 + win->pos.y + win->cursor.y, 1 + win->pos.x + win->cursor.x); + return ; +} + +void handle_escape_seq(const char *seq, t_window *win) +{ + const size_t seq_size = ft_strlen(seq); + int arg1; + + if (seq[0] == '\033') + { + if (seq_size >= 3 && seq[1] == '[') + { + arg1 = ft_atoi(seq + 2); + if (arg1 == 0) + arg1 = 1; + if (seq[seq_size - 1] == 'A') + move_cursor(win, 0, -arg1); + else if (seq[seq_size - 1] == 'B') + move_cursor(win, 0, arg1); + else if (seq[seq_size - 1] == 'C') + move_cursor(win, arg1, 0); + else if (seq[seq_size - 1] == 'D') + move_cursor(win, -arg1, 0); + } + } + return ; +} + +t_symbol *str_to_sym(const char *str) +{ + t_symbol *res; + t_symbol *cur; + size_t i; + + res = ft_calloc(sizeof(*res), ft_strlen(str) + 1); + if (!res) + return (NULL); + cur = res; + i = 0; + while(*str) + { + cur->sym[i] = *str; + ++i; + if (*str & 0x80) + { + ++cur; + i = 0; + } + ++str; } - if (!str_inp) - return (mode_exit); - process_input_string(input, str_inp); - free(str_inp); return (res); } -t_mode process_input(t_vec *nodes, t_vec *mosfets, int fd) +void draw_window(t_window *win) +{ + char *mat_ptr; + size_t i; + + ft_printf("\033[%u;%uH", 1 + (unsigned int)win->pos.y, 1 + (unsigned int)win->pos.x); + i = 0; + while (i < win->size.y) + { + mat_ptr = ft_mat_access(&win->content, win->pos.y + i, win->pos.x); + write(STDOUT_FILENO, mat_ptr, win->size.x * SYMBOL_SIZE); + ++i; + } + ft_printf("\033[%u;%uH", 1 + (unsigned int)(win->pos.y + win->cursor.y), 1 + (unsigned int)(win->pos.x + win->cursor.x)); + return ; +} + +void print_to_win(t_window *win, const char *str) { - t_mode res; - int success; - static t_input input = {.command = help, .argc = 0}; - - ft_printf("FET_sim> "); - res = get_input(&input, fd); - if (res != mode_command) - return (res); - success = 1; - if (input.command == next) - success = c_next(input, nodes, mosfets); - else if (input.command == draw) - success = c_draw(input, mosfets); - else if (input.command == setnode) - success = c_setnode(input, nodes); - else if (input.command == addnode) - success = c_addnode(input, nodes); - else if (input.command == addfet) - success = c_addfet(input, mosfets); - else if (input.command == bind) - success = c_bind(input, nodes, mosfets); - else if (input.command == help) - success = c_help(input); - else if (input.command == exitsim) - res = mode_exit; - if (success) - return (res); - return (mode_error); + t_symbol *sym_str; + size_t i; + + sym_str = str_to_sym(str); + if (win->type != mode_command) + return ; + move_cursor(win, 0, 1); + win->cursor.x = 0; + i = 0; + while (sym_str[i].sym[0] != '\0') + { + if (win->cursor.x == win->size.x || sym_str[i].sym[0] == '\n') + { + move_cursor(win, 0, 1); + win->cursor.x = 0; + } + if (sym_str[i].sym[0] != '\n') + { + *(t_symbol *)ft_mat_access(&win->content, win->cursor.y, win->cursor.x) = sym_str[i]; + ++win->cursor.x; + } + ++i; + } + free(sym_str); + draw_window(win); + return ; +} + +t_window *process_input_schema(const char *input, t_tui *tui, __attribute__((unused)) t_vec *nodes, __attribute__((unused)) t_vec *mosfets) +{ + if (*input == '\033') + handle_escape_seq(input, tui->active_win); + else + { + while (*input != 0) + { + if (*input == 'l') + move_cursor(tui->active_win, 1, 0); + else if (*input == 'k') + move_cursor(tui->active_win, 0, -1); + else if (*input == 'j') + move_cursor(tui->active_win, 0, 1); + else if (*input == 'h') + move_cursor(tui->active_win, -1, 0); + else if (*input == 'i') + { + if (*(++input) != 0) + print_to_win(ft_vec_access(&tui->windows, MAIN_COMMAND_W), + "Rest of the input was lost.\n"); + return (ft_vec_access(&tui->windows, MAIN_COMMAND_W)); + } + ++input; + } + } + return (tui->active_win); +} + +void loop(t_tui *tui, t_vec *nodes, t_vec *mosfets) +{ + size_t i; + char buffer[BUFFER_SIZE + 1]; + + print_start(ft_vec_access(&tui->windows, 0)); + i = 1; + while (i < tui->windows.size) + draw_window(ft_vec_access(&tui->windows, i++)); + while (tui->active_win) + { + i = read(STDIN_FILENO, buffer, BUFFER_SIZE); + buffer[i] = '\0'; + if (tui->active_win->type == mode_schema) + tui->active_win = process_input_schema(buffer, tui, nodes, mosfets); + else if (tui->active_win->type == mode_command) + tui->active_win = process_input_command(buffer, tui, nodes, mosfets); + } + return ; +} + +void cleanup_tui(t_tui *tui) +{ + ft_vec_free(&tui->windows, destroy_win); + return ; } -void cleanup(t_vec *nodes, t_vec *mosfets) +void cleanup(t_vec *nodes, t_vec *mosfets, t_tui *tui) { + cleanup_tui(tui); update_nodes(NULL); ft_vec_free(nodes, free_node); ft_vec_free(mosfets, NULL); @@ -308,26 +582,21 @@ void cleanup(t_vec *nodes, t_vec *mosfets) return ; } -int main(int argc, char **argv) +int main(__attribute__((unused))int argc, __attribute__((unused)) char **argv) { t_vec nodes; t_vec mosfets; - t_mode mode; + t_tui tui; setup_terminal(); - print_start(); ft_vec_init(&nodes, sizeof(t_node)); ft_vec_init(&mosfets, sizeof(t_mosfet)); - if (argc > 1) - build_graph(argv[1], &nodes, &mosfets); - mode = mode_command; - while (mode != mode_exit && mode != mode_error) + if (!init_tui(&tui)) { - if (mode == mode_command) - mode = process_input(&nodes, &mosfets, STDIN_FILENO); -// else if (mode == mode_schema) -// mode = schema_mode(&nodes, &mosfets, STDIN_FILENO); + //if (argc == 2) + //build_graph(argv[1], &nodes, &mosfets, &tui); + loop(&tui, &nodes, &mosfets); } - cleanup(&nodes, &mosfets); + cleanup(&nodes, &mosfets, &tui); return (0); } diff --git a/src/terminal.c b/src/terminal.c index 1b97709..7c9a4a3 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -15,16 +15,47 @@ static void termios_settings(void) tcgetattr(STDIN_FILENO, &termios_set); tcgetattr(STDIN_FILENO, &init_termios_set); - termios_set.c_lflag &= !ICANON; + termios_set.c_lflag &= ~(ICANON | ECHO); termios_set.c_cc[VMIN] = 1; termios_set.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &termios_set); return ; } +#define TERMSIZE_BUFFER_SIZE 12 + +static const char *term_size_str = "" + "\033[s" // Save cursor position + "\033[9999;9999H" // Move cursor as far to the right bottom as possible + "\033[6n" // Report cursor position + ; + +static const char *restore_cursor = "\033[u"; + +t_pair get_terminal_size(void) +{ + char buffer[TERMSIZE_BUFFER_SIZE]; + size_t i; + t_pair res; + + res.x = 0; + res.y = 0; + ft_putstr_fd(term_size_str, STDOUT_FILENO); + if (read(STDIN_FILENO, buffer, TERMSIZE_BUFFER_SIZE) < 0) + return (res); + res.y = ft_atoi(&buffer[2]) - 1; + i = 0; + while (buffer[i] != ';') + ++i; + ++i; + res.x = ft_atoi(&buffer[i]); + ft_putstr_fd(restore_cursor, STDOUT_FILENO); + return (res); +} + void setup_terminal(void) { - ft_printf("%s", alt_term_on); + ft_printf(alt_term_on); ft_printf("\033[H"); termios_settings(); return ; @@ -32,7 +63,7 @@ void setup_terminal(void) void clean_terminal(void) { - ft_printf("%s", alt_term_off); + ft_printf(alt_term_off); tcsetattr(STDIN_FILENO, TCSANOW, &init_termios_set); return ; } diff --git a/src/text.c b/src/text.c index b46ca15..9494b92 100644 --- a/src/text.c +++ b/src/text.c @@ -1,7 +1,7 @@ #include "FET_sim.h" #include "libft.h" -static const char g_version_str[] = "0.0"; +#define VERSION "0.1" static const char g_next_help_str[] = "" "next [STEPS]\n" @@ -58,8 +58,9 @@ static const char g_help_exit_str[] = "" static const char g_general_help_str[] = "" "This is a FET_sim - simulator of FET (Field Effect Transistor) logic.\n" - "Version number: %s" - "You can use the following commands:\n\n" + "Version " + VERSION + "\nYou can use the following commands:\n\n" "next [STEPS] \t\t- advances the simulation\n" "draw [IND] \t\t- draws the state of the simulation\n" "setnode IND STATE \t- sets the \"set_state\" of the node indexed by IND to STATE\n" @@ -69,55 +70,56 @@ static const char g_general_help_str[] = "" "help [COMMAND] \t\t- shows this help or help for COMMAND\n" "exit [...] \t\t- exits this program\n\n"; -void print_help(t_command c) +void print_help(t_window *win, t_command c) { if (c == next) - ft_printf(g_next_help_str); + print_to_win(win, g_next_help_str); else if (c == draw) - ft_printf(g_draw_help_str); + print_to_win(win, g_draw_help_str); else if (c == setnode) - ft_printf(g_setnode_help_str); + print_to_win(win, g_setnode_help_str); else if (c == addnode) - ft_printf(g_addnode_help_str); + print_to_win(win, g_addnode_help_str); else if (c == addfet) - ft_printf(g_addfet_help_str); + print_to_win(win, g_addfet_help_str); else if (c == bind) - ft_printf(g_bind_help_str); + print_to_win(win, g_bind_help_str); else if (c == help) - ft_printf(g_help_help_str); + print_to_win(win, g_help_help_str); else if (c == exitsim) - ft_printf(g_help_exit_str); + print_to_win(win, g_help_exit_str); else - ft_printf(g_general_help_str, g_version_str); + print_to_win(win, g_general_help_str); return ; } -void command_not_found(const char *input) +void command_not_found(t_window *win, __attribute__((unused))const char *input) { - ft_printf("The command \"%s\" is not a proper FET_sim command.\n", input); + print_to_win(win, "The command \"%s\" is not a proper FET_sim command.\n"); + //, input); return ; } -void print_start(void) +void print_start(t_window *win) { - ft_printf("FET_sim, version %s\n" - "FET_sim is a simulator of FET logic.\n" - "If you need some guidence, use the \"help\" command.\n", - g_version_str); + print_to_win(win, "FET_sim, version " + VERSION + "\nFET_sim is a simulator of FET logic.\n" + "If you need some guidence, use the \"help\" command.\n"); return ; } -void print_index_error(size_t index, size_t size) +void print_index_error(t_window *win, __attribute__((unused))size_t index, __attribute__((unused))size_t size) { if (size > 0) { - ft_printf("This action is invalid as given index %u " - "is larger then the highest index present %u.\n", - index, size - 1); + print_to_win(win, "This action is invalid as given index %u " + "is larger then the highest index present %u.\n"); + //,index, size - 1); } else { - ft_printf("This action is invalid as there is no element yet.\n"); + print_to_win(win, "This action is invalid as there is no element yet.\n"); } return ; } -- 2.30.2