aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2015-08-26 09:07:04 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2015-09-06 09:18:52 -0300
commitf5c5cdb306704e40019fb0eb9457e64bda8ee7cc (patch)
tree07bc347ee6f3af269710e42e3d40aa3c06190489
parentcb9ae4e373af5d436499531698cec96ffc9bc39d (diff)
downloadrneovim-f5c5cdb306704e40019fb0eb9457e64bda8ee7cc.tar.gz
rneovim-f5c5cdb306704e40019fb0eb9457e64bda8ee7cc.tar.bz2
rneovim-f5c5cdb306704e40019fb0eb9457e64bda8ee7cc.zip
tui: Move screen state tracking to new "ugrid" module
The ugrid module implements a unicode "drawing" grid and is used to store TUI screen state. Later this module will be reused in other layers.
-rw-r--r--src/nvim/tui/tui.c208
-rw-r--r--src/nvim/ugrid.c137
-rw-r--r--src/nvim/ugrid.h40
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