diff options
| author | A404M <ahmadmahmoudiprogrammer@gmail.com> | 2024-08-28 04:38:29 +0330 | 
|---|---|---|
| committer | A404M <ahmadmahmoudiprogrammer@gmail.com> | 2024-08-28 04:38:29 +0330 | 
| commit | 4c49de0cb60179f20fc579dfdba5aeca20be8abc (patch) | |
| tree | 5034bafc384778c7041abb2cdb6819f9473b5291 | |
| parent | e9d373a154f538ef316940a142f652dba1a9bea6 (diff) | |
fixed screen glitching
optimized the code
| -rw-r--r-- | src/ui/tui.c | 187 | ||||
| -rw-r--r-- | src/ui/tui.h | 47 | 
2 files changed, 168 insertions, 66 deletions
diff --git a/src/ui/tui.c b/src/ui/tui.c index 8ed59ce..0c88fde 100644 --- a/src/ui/tui.c +++ b/src/ui/tui.c @@ -29,11 +29,32 @@ void _tui_delete_callbacks(TUI *tui, int width) {    free(tui->on_click_callbacks);  } +void _tui_clear_cells(TUI *tui) { +  const TERMINAL_CELL empty = {.c = ' ', +                               .color = COLOR_NO_COLOR, +                               .background_color = COLOR_NO_COLOR, +                               .on_click_callback = NULL}; +  for (int i = 0; i < tui->cells_length; ++i) { +    tui->cells[i] = empty; +  } +} + +void _tui_init_cells(TUI *tui) { +  tui->cells_length = tui_get_width(tui) * tui_get_height(tui); +  tui->cells = malloc(tui->cells_length * sizeof(TERMINAL_CELL)); +  _tui_clear_cells(tui); +} + +void _tui_delete_cells(TUI *tui) { +  tui->cells_length = 0; +  free(tui->cells); +  tui->cells = NULL; +} +  TUI *tui_init() {    setbuf(stdout, NULL);    TUI *tui = malloc(sizeof(TUI)); -  tui->buffer = malloc(0);    tui_get_cursor_pos(tui, &tui->init_cursor_x, &tui->init_cursor_y); @@ -52,6 +73,7 @@ TUI *tui_init() {    write(STDOUT_FILENO, "\e[?9h", 5);    _tui_init_callbacks(tui); +  _tui_init_cells(tui);    tui_refresh(tui);    return tui; @@ -81,7 +103,9 @@ void tui_refresh(TUI *tui) {      return;    }    _tui_delete_callbacks(tui, width); +  _tui_delete_cells(tui);    _tui_init_callbacks(tui); +  _tui_init_cells(tui);  }  int tui_get_width(TUI *tui) { return tui->size.ws_col; } @@ -125,9 +149,29 @@ int tui_delete_before() { return printf("\b \b"); }  int tui_delete_under_cursor() { return printf(" \b"); } -void tui_set_on_click_callback(TUI *tui, int x, int y, -                               ON_CLICK_CALLBACK callback) { -  tui->on_click_callbacks[x][y] = callback; +int _tui_get_cell_index(TUI *tui, int x, int y) { +  const int width = tui_get_width(tui); +  return x + width * y; +} + +void _tui_set_cell_char(TUI *tui, int x, int y, char c) { +  tui->cells[_tui_get_cell_index(tui, x, y)].c = c; +} + +void _tui_set_cell_color(TUI *tui, int x, int y, COLOR color) { +  tui->cells[_tui_get_cell_index(tui, x, y)].color = color; +} + +void _tui_set_cell_background_color(TUI *tui, int x, int y, +                                    COLOR background_color) { +  tui->cells[_tui_get_cell_index(tui, x, y)].background_color = +      background_color; +} + +void _tui_set_cell_on_click_callback(TUI *tui, int x, int y, +                                     ON_CLICK_CALLBACK on_click_callback) { +  tui->cells[_tui_get_cell_index(tui, x, y)].on_click_callback = +      on_click_callback;  }  void tui_handle_mouse_action(TUI *tui, MOUSE_ACTION mouse_action) { @@ -215,9 +259,9 @@ void tui_start_app(TUI *tui, WIDGET_BUILDER widget_builder) {    tui_main_loop(tui, widget_builder);  } -void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin, -                      int width_end, int height_begin, int height_end, -                      COLOR parent_color, int *child_width, int *child_height) { +void _tui_draw_widget_to_cells(TUI *tui, const WIDGET *widget, int width_begin, +                               int width_end, int height_begin, int height_end, +                               int *child_width, int *child_height) {    switch (widget->type) {    case WIDGET_TYPE_TEXT: {      const TEXT_METADATA *metadata = widget->metadata; @@ -225,21 +269,20 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,      const int textLen = strlen(metadata->text);      int height = height_begin;      for (; height < height_end; ++height) { -      tui_move_to(width_begin, height);        const int begin = (height - height_begin) * widthDiff; -      int end = begin + widthDiff; -      int shouldExit = 0; -      if (end > textLen) { -        end = textLen; -        shouldExit = 1; +      const int end = begin + widthDiff; + +      for (int j = 0; j < end; ++j) { +        const int x = width_begin + j; +        const int y = height; +        _tui_set_cell_color(tui, x, y, metadata->color); +        const int index = begin + j; +        if (index < textLen) { +          _tui_set_cell_char(tui, x, y, metadata->text[index]); +        }        } -      tui_change_terminal_text_color(metadata->color); -      tui_change_terminal_background_color(parent_color); -      write(STDOUT_FILENO, metadata->text + begin, end - begin); -      tui_change_terminal_background_color(COLOR_RESET); -      tui_change_terminal_text_color(COLOR_RESET); -      if (shouldExit) { +      if (end > textLen) {          break;        }      } @@ -249,16 +292,13 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,    } break;    case WIDGET_TYPE_BUTTON: {      const BUTTON_METADATA *metadata = widget->metadata; -    tui_move_to(width_begin, height_begin);      if (metadata->child != NULL) { -      _tui_draw_widget(tui, metadata->child, width_begin, width_end, -                       height_begin, height_end, parent_color, child_width, -                       child_height); -      /*printf("%d,%d\n\r",*child_width,*child_height);*/ -      /*sleep(1);*/ +      _tui_draw_widget_to_cells(tui, metadata->child, width_begin, width_end, +                                height_begin, height_end, child_width, +                                child_height);        for (int i = width_begin; i < *child_width; ++i) {          for (int j = height_begin; j < *child_height; ++j) { -          tui_set_on_click_callback(tui, i, j, metadata->callback); +          _tui_set_cell_on_click_callback(tui, i, j, metadata->callback);          }        }      } @@ -270,8 +310,9 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,      for (int i = 0; i < metadata->children->size; ++i) {        const WIDGET *child = metadata->children->widgets[i];        int width_temp; -      _tui_draw_widget(tui, child, width_begin, width_end, *child_height, -                       height_end, parent_color, &width_temp, child_height); +      _tui_draw_widget_to_cells(tui, child, width_begin, width_end, +                                *child_height, height_end, &width_temp, +                                child_height);        if (width_temp > *child_width) {          *child_width = width_temp;        } @@ -284,8 +325,9 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,      for (int i = 0; i < metadata->children->size; ++i) {        const WIDGET *child = metadata->children->widgets[i];        int height_temp; -      _tui_draw_widget(tui, child, *child_width, width_end, height_begin, -                       height_end, parent_color, child_width, &height_temp); +      _tui_draw_widget_to_cells(tui, child, *child_width, width_end, +                                height_begin, height_end, child_width, +                                &height_temp);        if (height_temp > *child_height) {          *child_height = height_temp;        } @@ -306,18 +348,15 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,      }      for (int y = height_begin; y < height_end; ++y) { -      tui_move_to(width_begin, y);        for (int x = width_begin; x < width_end; ++x) { -        tui_change_terminal_background_color(metadata->color); -        write(STDOUT_FILENO, " ", 1); -        tui_change_terminal_background_color(COLOR_RESET); +        _tui_set_cell_background_color(tui, x, y, metadata->color);        }      }      if (metadata->child != NULL) {        int t0, t1; -      _tui_draw_widget(tui, metadata->child, width_begin, width_end, -                       height_begin, height_end, metadata->color, &t0, &t1); +      _tui_draw_widget_to_cells(tui, metadata->child, width_begin, width_end, +                                height_begin, height_end, &t0, &t1);      }      *child_width = width_end;      *child_height = height_end; @@ -329,21 +368,71 @@ void _tui_draw_widget(TUI *tui, const WIDGET *widget, int width_begin,    }  } +int _tui_get_background_color_ascii(COLOR color) { +  if (color == COLOR_NO_COLOR) { +    return 0; +  } else if (color == COLOR_RESET) { +    return printf("\033[%dm", COLOR_RESET); +  } +  return printf("\033[%dm", color + 40); +} + +void _tui_draw_cells_to_terminal(TUI *tui) { +  const size_t size_of_cell = 5 + 5 + sizeof(char) + 5; +  const size_t size = tui->cells_length * (size_of_cell); +  char str[(size + 1) * sizeof(char)]; +  str[0] = '\0'; +  char cell_str[5]; + +  COLOR last_color = COLOR_NO_COLOR; +  COLOR last_background_color = COLOR_NO_COLOR; + +  for (int i = 0; i < tui->cells_length; ++i) { +    const TERMINAL_CELL cell = tui->cells[i]; + +    if (last_color != cell.color || +        last_background_color != cell.background_color) { +      sprintf(cell_str, "\033[%dm", COLOR_RESET); +      strcat(str, cell_str); +      last_color = cell.color; // TODO: run to know what to fix +      last_background_color = cell.background_color; +      if (cell.color == COLOR_RESET || cell.color == COLOR_NO_COLOR) { +        sprintf(cell_str, "\033[%dm", COLOR_RESET); +      } else { +        sprintf(cell_str, "\033[%dm", cell.color + 30); +      } +      strcat(str, cell_str); + +      if (cell.background_color == COLOR_RESET || +          cell.background_color == COLOR_NO_COLOR) { +        sprintf(cell_str, "\033[%dm", COLOR_RESET); +      } else { +        sprintf(cell_str, "\033[%dm", cell.background_color + 40); +      } +      strcat(str, cell_str); +    } +    strncat(str, &cell.c, 1); +  } +  const int len = strlen(str); + +  tui_move_to(0, 0); +  write(STDOUT_FILENO, str, len); +} +  void tui_main_loop(TUI *tui, WIDGET_BUILDER widget_builder) {    while (1) {      clock_t start = clock();      tui_refresh(tui); -    tui_save_cursor(); -    tui_clear_screen();      WIDGET *root_widget = widget_builder(tui); +    tui_save_cursor(); +    _tui_clear_cells(tui);      int width, height; -    _tui_draw_widget(tui, root_widget, 0, tui_get_width(tui), 0, -                     tui_get_height(tui), COLOR_NO_COLOR, &width, &height); +    _tui_draw_widget_to_cells(tui, root_widget, 0, tui_get_width(tui), 0, +                              tui_get_height(tui), &width, &height); +    _tui_draw_cells_to_terminal(tui);      tui_delete_widget(root_widget); -    tui_move_to(30, 30); -    printf("time:%ld",clock()-start);      tui_restore_cursor();      if (handle_input(tui)) {        return; @@ -359,6 +448,9 @@ WIDGET *tui_new_widget(WIDGET_TYPE type, void *metadata) {  }  void tui_delete_widget(WIDGET *widget) { +  if (widget == NULL) { +    return; +  }    switch (widget->type) {    case WIDGET_TYPE_TEXT:      _tui_delete_text(widget); @@ -438,8 +530,7 @@ void _tui_delete_column(WIDGET *column) {  }  WIDGET *tui_make_row(WIDGET_ARRAY *children) { -  return tui_new_widget(WIDGET_TYPE_ROW, -                        _tui_make_row_metadata(children)); +  return tui_new_widget(WIDGET_TYPE_ROW, _tui_make_row_metadata(children));  }  ROW_METADATA *_tui_make_row_metadata(WIDGET_ARRAY *children) { @@ -477,14 +568,14 @@ void _tui_delete_box(WIDGET *box) {    free(box->metadata);  } -WIDGET_ARRAY *tui_make_widget_array(int size,...){ +WIDGET_ARRAY *tui_make_widget_array(int size, ...) {    va_list arg_pointer;    va_start(arg_pointer, size); -  WIDGET **widgets = malloc(size*sizeof(WIDGET**)); +  WIDGET **widgets = malloc(size * sizeof(WIDGET **)); -  for(int i = 0;i < size;++i){ -    widgets[i] = va_arg(arg_pointer,WIDGET*); +  for (int i = 0; i < size; ++i) { +    widgets[i] = va_arg(arg_pointer, WIDGET *);    }    va_end(arg_pointer); diff --git a/src/ui/tui.h b/src/ui/tui.h index a3e64f3..15b65d6 100644 --- a/src/ui/tui.h +++ b/src/ui/tui.h @@ -21,12 +21,36 @@ typedef struct MOUSE_ACTION {  typedef void (*ON_CLICK_CALLBACK)(MOUSE_ACTION mouse_action); +#ifndef __cplusplus +typedef enum bool { false = 0, true = 1 } bool; +#endif + +typedef enum COLOR { +  COLOR_NO_COLOR = -1, +  COLOR_RESET = 0, +  COLOR_RED = 1, +  COLOR_GREEN = 2, +  COLOR_YELLOW = 3, +  COLOR_BLUE = 4, +  COLOR_MAGENTA = 5, +  COLOR_CYAN = 6, +  COLOR_WHITE = 7 +} COLOR; + +typedef struct TERMINAL_CELL { +  char c; +  COLOR color; +  COLOR background_color; +  ON_CLICK_CALLBACK on_click_callback; +} TERMINAL_CELL; +  typedef struct TUI {    struct winsize size;    struct termios original, raw, helper;    ON_CLICK_CALLBACK **on_click_callbacks;    int init_cursor_x, init_cursor_y; -  char *buffer; +  TERMINAL_CELL *cells; +  size_t cells_length;  } TUI;  typedef enum WIDGET_TYPE { @@ -42,20 +66,8 @@ typedef struct WIDGET {    void *metadata;  } WIDGET; -typedef enum COLOR { -  COLOR_NO_COLOR = -1, -  COLOR_RESET = 0, -  COLOR_RED = 1, -  COLOR_GREEN = 2, -  COLOR_YELLOW = 3, -  COLOR_BLUE = 4, -  COLOR_MAGENTA = 5, -  COLOR_CYAN = 6, -  COLOR_WHITE = 7 -} COLOR; -  typedef struct WIDGET_ARRAY { -  int size; +  size_t size;    WIDGET **widgets;  } WIDGET_ARRAY; @@ -99,10 +111,9 @@ extern int tui_move_to(int x, int y);  extern int tui_clear_screen();  extern void tui_start_app(TUI *tui, WIDGET_BUILDER widget_builder); -extern void _tui_draw_widget(TUI *tui, const WIDGET *widget, int widthBegin, -                             int widthEnd, int heightBegin, int heightEnd, -                             COLOR parent_color, int *childWidth, -                             int *childHeight); +extern void _tui_draw_widget_to_cells(TUI *tui, const WIDGET *widget, int width_begin, +                             int width_end, int height_begin, int height_end, +                             int *child_width, int *childHeight);  extern void tui_main_loop(TUI *tui, WIDGET_BUILDER widget_builder);  extern WIDGET *tui_new_widget(WIDGET_TYPE type, void *metadata);  |