diff options
| -rw-r--r-- | src/nvim/tui/tui.c | 208 | ||||
| -rw-r--r-- | src/nvim/ugrid.c | 137 | ||||
| -rw-r--r-- | src/nvim/ugrid.h | 40 | 
3 files changed, 244 insertions, 141 deletions
| diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 235fa3117f..a5a6783927 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -21,6 +21,7 @@  #include "nvim/os/input.h"  #include "nvim/os/os.h"  #include "nvim/strings.h" +#include "nvim/ugrid.h"  // Space reserved in the output buffer to restore the cursor to normal when  // flushing. No existing terminal will require 32 bytes to do that. @@ -32,11 +33,6 @@ typedef struct {  } Rect;  typedef struct { -  char data[7]; -  HlAttrs attrs; -} Cell; - -typedef struct {    unibi_var_t params[9];    char buf[OUTBUF_SIZE];    size_t bufpos, bufsize; @@ -45,17 +41,13 @@ typedef struct {    unibi_term *ut;    uv_tty_t output_handle;    SignalWatcher winch_handle; -  Rect scroll_region; +  UGrid grid;    kvec_t(Rect) invalid_regions; -  int row, col; -  int bg, fg;    int out_fd; -  int old_height;    bool can_use_terminal_scroll;    bool mouse_enabled;    bool busy; -  HlAttrs attrs, print_attrs; -  Cell **screen; +  HlAttrs print_attrs;    int showing_mode;    struct {      int enable_mouse, disable_mouse; @@ -71,32 +63,14 @@ static bool volatile got_winch = false;  # include "tui/tui.c.generated.h"  #endif -#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1}) - -#define FOREACH_CELL(ui, top, bot, left, right, go, code)               \ -  do {                                                                  \ -    TUIData *data = ui->data;                                           \ -    for (int row = top; row <= bot; ++row) {                            \ -      Cell *cells = data->screen[row];                                  \ -      if (go) {                                                         \ -        unibi_goto(ui, row, left);                                      \ -      }                                                                 \ -      for (int col = left; col <= right; ++col) {                       \ -        Cell *cell = cells + col;                                       \ -        (void)(cell);                                                   \ -        code;                                                           \ -      }                                                                 \ -    }                                                                   \ -  } while (0) -  UI *tui_start(void)  {    TUIData *data = xcalloc(1, sizeof(TUIData));    UI *ui = xcalloc(1, sizeof(UI));    ui->data = data; -  data->attrs = data->print_attrs = EMPTY_ATTRS; -  data->fg = data->bg = -1; +  data->print_attrs = EMPTY_ATTRS; +  ugrid_init(&data->grid);    data->can_use_terminal_scroll = true;    data->bufpos = 0;    data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE; @@ -201,7 +175,7 @@ static void tui_stop(UI *ui)    }    xfree(data->write_loop);    unibi_destroy(data->ut); -  destroy_screen(data); +  ugrid_free(&data->grid);    xfree(data);    ui_detach(ui);    xfree(ui); @@ -233,9 +207,10 @@ static void update_attrs(UI *ui, HlAttrs attrs)    data->print_attrs = attrs;    unibi_out(ui, unibi_exit_attribute_mode); +  UGrid *grid = &data->grid; -  int fg = attrs.foreground != -1 ? attrs.foreground : data->fg; -  int bg = attrs.background != -1 ? attrs.background : data->bg; +  int fg = attrs.foreground != -1 ? attrs.foreground : grid->fg; +  int bg = attrs.background != -1 ? attrs.background : grid->bg;    if (ui->rgb) {      if (fg != -1) { @@ -277,25 +252,25 @@ static void update_attrs(UI *ui, HlAttrs attrs)    }  } -static void print_cell(UI *ui, Cell *ptr) +static void print_cell(UI *ui, UCell *ptr)  {    update_attrs(ui, ptr->attrs);    out(ui, ptr->data, strlen(ptr->data));  } -static void clear_region(UI *ui, int top, int bot, int left, int right, -    bool refresh) +static void clear_region(UI *ui, int top, int bot, int left, int right)  {    TUIData *data = ui->data; -  HlAttrs clear_attrs = EMPTY_ATTRS; -  clear_attrs.foreground = data->fg; -  clear_attrs.background = data->bg; -  update_attrs(ui, clear_attrs); +  UGrid *grid = &data->grid;    bool cleared = false; -  if (refresh && data->bg == -1 && right == ui->width -1) { +  if (grid->bg == -1 && right == ui->width -1) {      // Background is set to the default color and the right edge matches the      // screen end, try to use terminal codes for clearing the requested area. +    HlAttrs clear_attrs = EMPTY_ATTRS; +    clear_attrs.foreground = grid->fg; +    clear_attrs.background = grid->bg; +    update_attrs(ui, clear_attrs);      if (left == 0) {        if (bot == ui->height - 1) {          if (top == 0) { @@ -318,36 +293,26 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,      }    } -  bool clear = refresh && !cleared; -  FOREACH_CELL(ui, top, bot, left, right, clear, { -    cell->data[0] = ' '; -    cell->data[1] = 0; -    cell->attrs = clear_attrs; -    if (clear) { +  if (!cleared) { +    // could not clear using faster terminal codes, refresh the whole region +    int currow = -1; +    UGRID_FOREACH_CELL(grid, top, bot, left, right, { +      if (currow != row) { +        unibi_goto(ui, row, col); +        currow = row; +      }        print_cell(ui, cell); -    } -  }); +    }); +  }    // restore cursor -  unibi_goto(ui, data->row, data->col); +  unibi_goto(ui, grid->row, grid->col);  }  static void tui_resize(UI *ui, int width, int height)  {    TUIData *data = ui->data; -  destroy_screen(data); - -  data->screen = xmalloc((size_t)height * sizeof(Cell *)); -  for (int i = 0; i < height; i++) { -    data->screen[i] = xcalloc((size_t)width, sizeof(Cell)); -  } - -  data->old_height = height; -  data->scroll_region.top = 0; -  data->scroll_region.bot = height - 1; -  data->scroll_region.left = 0; -  data->scroll_region.right = width - 1; -  data->row = data->col = 0; +  ugrid_resize(&data->grid, width, height);    if (!got_winch) {  // Try to resize the terminal window.      char r[16];  // enough for 9999x9999 @@ -361,22 +326,23 @@ static void tui_resize(UI *ui, int width, int height)  static void tui_clear(UI *ui)  {    TUIData *data = ui->data; -  clear_region(ui, data->scroll_region.top, data->scroll_region.bot, -      data->scroll_region.left, data->scroll_region.right, true); +  UGrid *grid = &data->grid; +  ugrid_clear(grid); +  clear_region(ui, grid->top, grid->bot, grid->left, grid->right);  }  static void tui_eol_clear(UI *ui)  {    TUIData *data = ui->data; -  clear_region(ui, data->row, data->row, data->col, -      data->scroll_region.right, true); +  UGrid *grid = &data->grid; +  ugrid_eol_clear(grid); +  clear_region(ui, grid->row, grid->row, grid->col, grid->right);  }  static void tui_cursor_goto(UI *ui, int row, int col)  {    TUIData *data = ui->data; -  data->row = row; -  data->col = col; +  ugrid_goto(&data->grid, row, col);    unibi_goto(ui, row, col);  } @@ -434,11 +400,7 @@ static void tui_set_scroll_region(UI *ui, int top, int bot, int left,      int right)  {    TUIData *data = ui->data; -  data->scroll_region.top = top; -  data->scroll_region.bot = bot; -  data->scroll_region.left = left; -  data->scroll_region.right = right; - +  ugrid_set_scroll_region(&data->grid, top, bot, left, right);    data->can_use_terminal_scroll =      left == 0 && right == ui->width - 1      && ((top == 0 && bot == ui->height - 1) @@ -448,31 +410,24 @@ static void tui_set_scroll_region(UI *ui, int top, int bot, int left,  static void tui_scroll(UI *ui, int count)  {    TUIData *data = ui->data; -  int top = data->scroll_region.top; -  int bot = data->scroll_region.bot; -  int left = data->scroll_region.left; -  int right = data->scroll_region.right; +  UGrid *grid = &data->grid; +  int clear_top, clear_bot; +  ugrid_scroll(grid, count, &clear_top, &clear_bot);    if (data->can_use_terminal_scroll) {      // Change terminal scroll region and move cursor to the top -    data->params[0].i = top; -    data->params[1].i = bot; +    data->params[0].i = grid->top; +    data->params[1].i = grid->bot;      unibi_out(ui, unibi_change_scroll_region); -    unibi_goto(ui, top, left); +    unibi_goto(ui, grid->top, grid->left);      // also set default color attributes or some terminals can become funny      HlAttrs clear_attrs = EMPTY_ATTRS; -    clear_attrs.foreground = data->fg; -    clear_attrs.background = data->bg; +    clear_attrs.foreground = grid->fg; +    clear_attrs.background = grid->bg;      update_attrs(ui, clear_attrs);    } -  // Compute start/stop/step for the loop below, also use terminal scroll -  // if possible -  int start, stop, step;    if (count > 0) { -    start = top; -    stop = bot - count + 1; -    step = 1;      if (data->can_use_terminal_scroll) {        if (count == 1) {          unibi_out(ui, unibi_delete_line); @@ -483,9 +438,6 @@ static void tui_scroll(UI *ui, int count)      }    } else { -    start = bot; -    stop = top - count - 1; -    step = -1;      if (data->can_use_terminal_scroll) {        if (count == -1) {          unibi_out(ui, unibi_insert_line); @@ -501,52 +453,30 @@ static void tui_scroll(UI *ui, int count)      data->params[0].i = 0;      data->params[1].i = ui->height - 1;      unibi_out(ui, unibi_change_scroll_region); -    unibi_goto(ui, data->row, data->col); -  } - -  int i; -  // Scroll internal screen -  for (i = start; i != stop; i += step) { -    Cell *target_row = data->screen[i] + left; -    Cell *source_row = data->screen[i + count] + left; -    memcpy(target_row, source_row, sizeof(Cell) * (size_t)(right - left + 1)); -  } - -  // clear emptied region, updating the terminal if its builtin scrolling -  // facility was used. This is done when the background color is not the -  // default, since scrolling may leave wrong background in the cleared area. -  bool update_clear = data->bg != -1 && data->can_use_terminal_scroll; -  if (count > 0) { -    clear_region(ui, stop, stop + count - 1, left, right, update_clear); +    unibi_goto(ui, grid->row, grid->col); + +    if (grid->bg != -1) { +      // Update the cleared area of the terminal if its builtin scrolling +      // facility was used and the background color is not the default. This is +      // required because scrolling may leave wrong background in the cleared +      // area. +      clear_region(ui, clear_top, clear_bot, grid->left, grid->right); +    }    } else { -    clear_region(ui, stop + count + 1, stop, left, right, update_clear); -  } - -  if (!data->can_use_terminal_scroll) {      // Mark the entire scroll region as invalid for redrawing later -    invalidate(ui, data->scroll_region.top, data->scroll_region.bot, -        data->scroll_region.left, data->scroll_region.right); +    invalidate(ui, grid->top, grid->bot, grid->left, grid->right);    }  }  static void tui_highlight_set(UI *ui, HlAttrs attrs)  { -  ((TUIData *)ui->data)->attrs = attrs; +  ((TUIData *)ui->data)->grid.attrs = attrs;  }  static void tui_put(UI *ui, uint8_t *text, size_t size)  {    TUIData *data = ui->data; -  Cell *cell = data->screen[data->row] + data->col; -  cell->data[size] = 0; -  cell->attrs = data->attrs; - -  if (text) { -    memcpy(cell->data, text, size); -  } - -  print_cell(ui, cell); -  data->col += 1; +  print_cell(ui, ugrid_put(&data->grid, text, size));  }  static void tui_bell(UI *ui) @@ -561,26 +491,32 @@ static void tui_visual_bell(UI *ui)  static void tui_update_fg(UI *ui, int fg)  { -  ((TUIData *)ui->data)->fg = fg; +  ((TUIData *)ui->data)->grid.fg = fg;  }  static void tui_update_bg(UI *ui, int bg)  { -  ((TUIData *)ui->data)->bg = bg; +  ((TUIData *)ui->data)->grid.bg = bg;  }  static void tui_flush(UI *ui)  {    TUIData *data = ui->data; +  UGrid *grid = &data->grid;    while (kv_size(data->invalid_regions)) {      Rect r = kv_pop(data->invalid_regions); -    FOREACH_CELL(ui, r.top, r.bot, r.left, r.right, true, { +    int currow = -1; +    UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, { +      if (currow != row) { +        unibi_goto(ui, row, col); +        currow = row; +      }        print_cell(ui, cell);      });    } -  unibi_goto(ui, data->row, data->col); +  unibi_goto(ui, grid->row, grid->col);    flush_buf(ui);  } @@ -890,13 +826,3 @@ static void flush_buf(UI *ui)      unibi_out(ui, unibi_cursor_invisible);    }  } - -static void destroy_screen(TUIData *data) -{ -  if (data->screen) { -    for (int i = 0; i < data->old_height; i++) { -      xfree(data->screen[i]); -    } -    xfree(data->screen); -  } -} diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c new file mode 100644 index 0000000000..127b18feb6 --- /dev/null +++ b/src/nvim/ugrid.c @@ -0,0 +1,137 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <limits.h> + +#include "nvim/vim.h" +#include "nvim/ui.h" +#include "nvim/ugrid.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ugrid.c.generated.h" +#endif + +void ugrid_init(UGrid *grid) +{ +  grid->attrs = EMPTY_ATTRS; +  grid->fg = grid->bg = -1; +  grid->cells = NULL; +} + +void ugrid_free(UGrid *grid) +{ +  destroy_cells(grid); +} + +void ugrid_resize(UGrid *grid, int width, int height) +{ +  destroy_cells(grid); +  grid->cells = xmalloc((size_t)height * sizeof(UCell *)); +  for (int i = 0; i < height; i++) { +    grid->cells[i] = xcalloc((size_t)width, sizeof(UCell)); +  } + +  grid->top = 0; +  grid->bot = height - 1; +  grid->left = 0; +  grid->right = width - 1; +  grid->row = grid->col = 0; +  grid->width = width; +  grid->height = height; +} + +void ugrid_clear(UGrid *grid) +{ +  clear_region(grid, grid->top, grid->bot, grid->left, grid->right); +} + +void ugrid_eol_clear(UGrid *grid) +{ +  clear_region(grid, grid->row, grid->row, grid->col, grid->right); +} + +void ugrid_goto(UGrid *grid, int row, int col) +{ +  grid->row = row; +  grid->col = col; +} + +void ugrid_set_scroll_region(UGrid *grid, int top, int bot, int left, int right) +{ +  grid->top = top; +  grid->bot = bot; +  grid->left = left; +  grid->right = right; +} + +void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot) +{ +  // Compute start/stop/step for the loop below +  int start, stop, step; +  if (count > 0) { +    start = grid->top; +    stop = grid->bot - count + 1; +    step = 1; +  } else { +    start = grid->bot; +    stop = grid->top - count - 1; +    step = -1; +  } + +  int i; + +  // Copy cell data +  for (i = start; i != stop; i += step) { +    UCell *target_row = grid->cells[i] + grid->left; +    UCell *source_row = grid->cells[i + count] + grid->left; +    memcpy(target_row, source_row, +        sizeof(UCell) * (size_t)(grid->right - grid->left + 1)); +  } + +  // clear cells in the emptied region, +  if (count > 0) { +    *clear_top = stop; +    *clear_bot = stop + count - 1; +  } else { +    *clear_bot = stop; +    *clear_top = stop + count + 1; +  } +  clear_region(grid, *clear_top, *clear_bot, grid->left, grid->right); +} + +UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size) +{ +  UCell *cell = grid->cells[grid->row] + grid->col; +  cell->data[size] = 0; +  cell->attrs = grid->attrs; + +  if (text) { +    memcpy(cell->data, text, size); +  } + +  grid->col += 1; +  return cell; +} + +static void clear_region(UGrid *grid, int top, int bot, int left, int right) +{ +  HlAttrs clear_attrs = EMPTY_ATTRS; +  clear_attrs.foreground = grid->fg; +  clear_attrs.background = grid->bg; +  UGRID_FOREACH_CELL(grid, top, bot, left, right, { +    cell->data[0] = ' '; +    cell->data[1] = 0; +    cell->attrs = clear_attrs; +  }); +} + +static void destroy_cells(UGrid *grid) +{ +  if (grid->cells) { +    for (int i = 0; i < grid->height; i++) { +      xfree(grid->cells[i]); +    } +    xfree(grid->cells); +  } +} + diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h new file mode 100644 index 0000000000..e41461fa16 --- /dev/null +++ b/src/nvim/ugrid.h @@ -0,0 +1,40 @@ +#ifndef NVIM_UGRID_H +#define NVIM_UGRID_H + +#include "nvim/ui.h" + +typedef struct ucell UCell; +typedef struct ugrid UGrid; + +struct ucell { +  char data[7]; +  HlAttrs attrs; +}; + +struct ugrid { +  int top, bot, left, right; +  int row, col; +  int bg, fg; +  int width, height; +  HlAttrs attrs; +  UCell **cells; +}; + +#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1}) + +#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code)           \ +  do {                                                                  \ +    for (int row = top; row <= bot; ++row) {                            \ +      UCell *row_cells = (grid)->cells[row];                            \ +      for (int col = left; col <= right; ++col) {                       \ +        UCell *cell = row_cells + col;                                  \ +        (void)(cell);                                                   \ +        code;                                                           \ +      }                                                                 \ +    }                                                                   \ +  } while (0) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ugrid.h.generated.h" +#endif +#endif  // NVIM_UGRID_H | 
