From 4c49de0cb60179f20fc579dfdba5aeca20be8abc Mon Sep 17 00:00:00 2001
From: A404M <ahmadmahmoudiprogrammer@gmail.com>
Date: Wed, 28 Aug 2024 04:38:29 +0330
Subject: fixed screen glitching optimized the code

---
 src/ui/tui.c | 187 ++++++++++++++++++++++++++++++++++++++++++++---------------
 src/ui/tui.h |  47 +++++++++------
 2 files changed, 168 insertions(+), 66 deletions(-)

(limited to 'src/ui')

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);
-- 
cgit v1.2.3