mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 14:08:32 +00:00
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.
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/ugrid.h"
|
||||||
|
|
||||||
// Space reserved in the output buffer to restore the cursor to normal when
|
// Space reserved in the output buffer to restore the cursor to normal when
|
||||||
// flushing. No existing terminal will require 32 bytes to do that.
|
// flushing. No existing terminal will require 32 bytes to do that.
|
||||||
@@ -31,11 +32,6 @@ typedef struct {
|
|||||||
int top, bot, left, right;
|
int top, bot, left, right;
|
||||||
} Rect;
|
} Rect;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char data[7];
|
|
||||||
HlAttrs attrs;
|
|
||||||
} Cell;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unibi_var_t params[9];
|
unibi_var_t params[9];
|
||||||
char buf[OUTBUF_SIZE];
|
char buf[OUTBUF_SIZE];
|
||||||
@@ -45,17 +41,13 @@ typedef struct {
|
|||||||
unibi_term *ut;
|
unibi_term *ut;
|
||||||
uv_tty_t output_handle;
|
uv_tty_t output_handle;
|
||||||
SignalWatcher winch_handle;
|
SignalWatcher winch_handle;
|
||||||
Rect scroll_region;
|
UGrid grid;
|
||||||
kvec_t(Rect) invalid_regions;
|
kvec_t(Rect) invalid_regions;
|
||||||
int row, col;
|
|
||||||
int bg, fg;
|
|
||||||
int out_fd;
|
int out_fd;
|
||||||
int old_height;
|
|
||||||
bool can_use_terminal_scroll;
|
bool can_use_terminal_scroll;
|
||||||
bool mouse_enabled;
|
bool mouse_enabled;
|
||||||
bool busy;
|
bool busy;
|
||||||
HlAttrs attrs, print_attrs;
|
HlAttrs print_attrs;
|
||||||
Cell **screen;
|
|
||||||
int showing_mode;
|
int showing_mode;
|
||||||
struct {
|
struct {
|
||||||
int enable_mouse, disable_mouse;
|
int enable_mouse, disable_mouse;
|
||||||
@@ -71,32 +63,14 @@ static bool volatile got_winch = false;
|
|||||||
# include "tui/tui.c.generated.h"
|
# include "tui/tui.c.generated.h"
|
||||||
#endif
|
#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)
|
UI *tui_start(void)
|
||||||
{
|
{
|
||||||
TUIData *data = xcalloc(1, sizeof(TUIData));
|
TUIData *data = xcalloc(1, sizeof(TUIData));
|
||||||
UI *ui = xcalloc(1, sizeof(UI));
|
UI *ui = xcalloc(1, sizeof(UI));
|
||||||
ui->data = data;
|
ui->data = data;
|
||||||
data->attrs = data->print_attrs = EMPTY_ATTRS;
|
data->print_attrs = EMPTY_ATTRS;
|
||||||
data->fg = data->bg = -1;
|
ugrid_init(&data->grid);
|
||||||
data->can_use_terminal_scroll = true;
|
data->can_use_terminal_scroll = true;
|
||||||
data->bufpos = 0;
|
data->bufpos = 0;
|
||||||
data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE;
|
data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE;
|
||||||
@@ -201,7 +175,7 @@ static void tui_stop(UI *ui)
|
|||||||
}
|
}
|
||||||
xfree(data->write_loop);
|
xfree(data->write_loop);
|
||||||
unibi_destroy(data->ut);
|
unibi_destroy(data->ut);
|
||||||
destroy_screen(data);
|
ugrid_free(&data->grid);
|
||||||
xfree(data);
|
xfree(data);
|
||||||
ui_detach(ui);
|
ui_detach(ui);
|
||||||
xfree(ui);
|
xfree(ui);
|
||||||
@@ -233,9 +207,10 @@ static void update_attrs(UI *ui, HlAttrs attrs)
|
|||||||
|
|
||||||
data->print_attrs = attrs;
|
data->print_attrs = attrs;
|
||||||
unibi_out(ui, unibi_exit_attribute_mode);
|
unibi_out(ui, unibi_exit_attribute_mode);
|
||||||
|
UGrid *grid = &data->grid;
|
||||||
|
|
||||||
int fg = attrs.foreground != -1 ? attrs.foreground : data->fg;
|
int fg = attrs.foreground != -1 ? attrs.foreground : grid->fg;
|
||||||
int bg = attrs.background != -1 ? attrs.background : data->bg;
|
int bg = attrs.background != -1 ? attrs.background : grid->bg;
|
||||||
|
|
||||||
if (ui->rgb) {
|
if (ui->rgb) {
|
||||||
if (fg != -1) {
|
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);
|
update_attrs(ui, ptr->attrs);
|
||||||
out(ui, ptr->data, strlen(ptr->data));
|
out(ui, ptr->data, strlen(ptr->data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_region(UI *ui, int top, int bot, int left, int right,
|
static void clear_region(UI *ui, int top, int bot, int left, int right)
|
||||||
bool refresh)
|
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
UGrid *grid = &data->grid;
|
||||||
clear_attrs.foreground = data->fg;
|
|
||||||
clear_attrs.background = data->bg;
|
|
||||||
update_attrs(ui, clear_attrs);
|
|
||||||
|
|
||||||
bool cleared = false;
|
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
|
// 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.
|
// 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 (left == 0) {
|
||||||
if (bot == ui->height - 1) {
|
if (bot == ui->height - 1) {
|
||||||
if (top == 0) {
|
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;
|
if (!cleared) {
|
||||||
FOREACH_CELL(ui, top, bot, left, right, clear, {
|
// could not clear using faster terminal codes, refresh the whole region
|
||||||
cell->data[0] = ' ';
|
int currow = -1;
|
||||||
cell->data[1] = 0;
|
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
|
||||||
cell->attrs = clear_attrs;
|
if (currow != row) {
|
||||||
if (clear) {
|
unibi_goto(ui, row, col);
|
||||||
|
currow = row;
|
||||||
|
}
|
||||||
print_cell(ui, cell);
|
print_cell(ui, cell);
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// restore cursor
|
// 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)
|
static void tui_resize(UI *ui, int width, int height)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
destroy_screen(data);
|
ugrid_resize(&data->grid, width, height);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (!got_winch) { // Try to resize the terminal window.
|
if (!got_winch) { // Try to resize the terminal window.
|
||||||
char r[16]; // enough for 9999x9999
|
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)
|
static void tui_clear(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
clear_region(ui, data->scroll_region.top, data->scroll_region.bot,
|
UGrid *grid = &data->grid;
|
||||||
data->scroll_region.left, data->scroll_region.right, true);
|
ugrid_clear(grid);
|
||||||
|
clear_region(ui, grid->top, grid->bot, grid->left, grid->right);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_eol_clear(UI *ui)
|
static void tui_eol_clear(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
clear_region(ui, data->row, data->row, data->col,
|
UGrid *grid = &data->grid;
|
||||||
data->scroll_region.right, true);
|
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)
|
static void tui_cursor_goto(UI *ui, int row, int col)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
data->row = row;
|
ugrid_goto(&data->grid, row, col);
|
||||||
data->col = col;
|
|
||||||
unibi_goto(ui, 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)
|
int right)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
data->scroll_region.top = top;
|
ugrid_set_scroll_region(&data->grid, top, bot, left, right);
|
||||||
data->scroll_region.bot = bot;
|
|
||||||
data->scroll_region.left = left;
|
|
||||||
data->scroll_region.right = right;
|
|
||||||
|
|
||||||
data->can_use_terminal_scroll =
|
data->can_use_terminal_scroll =
|
||||||
left == 0 && right == ui->width - 1
|
left == 0 && right == ui->width - 1
|
||||||
&& ((top == 0 && bot == ui->height - 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)
|
static void tui_scroll(UI *ui, int count)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
int top = data->scroll_region.top;
|
UGrid *grid = &data->grid;
|
||||||
int bot = data->scroll_region.bot;
|
int clear_top, clear_bot;
|
||||||
int left = data->scroll_region.left;
|
ugrid_scroll(grid, count, &clear_top, &clear_bot);
|
||||||
int right = data->scroll_region.right;
|
|
||||||
|
|
||||||
if (data->can_use_terminal_scroll) {
|
if (data->can_use_terminal_scroll) {
|
||||||
// Change terminal scroll region and move cursor to the top
|
// Change terminal scroll region and move cursor to the top
|
||||||
data->params[0].i = top;
|
data->params[0].i = grid->top;
|
||||||
data->params[1].i = bot;
|
data->params[1].i = grid->bot;
|
||||||
unibi_out(ui, unibi_change_scroll_region);
|
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
|
// also set default color attributes or some terminals can become funny
|
||||||
HlAttrs clear_attrs = EMPTY_ATTRS;
|
HlAttrs clear_attrs = EMPTY_ATTRS;
|
||||||
clear_attrs.foreground = data->fg;
|
clear_attrs.foreground = grid->fg;
|
||||||
clear_attrs.background = data->bg;
|
clear_attrs.background = grid->bg;
|
||||||
update_attrs(ui, clear_attrs);
|
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) {
|
if (count > 0) {
|
||||||
start = top;
|
|
||||||
stop = bot - count + 1;
|
|
||||||
step = 1;
|
|
||||||
if (data->can_use_terminal_scroll) {
|
if (data->can_use_terminal_scroll) {
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
unibi_out(ui, unibi_delete_line);
|
unibi_out(ui, unibi_delete_line);
|
||||||
@@ -483,9 +438,6 @@ static void tui_scroll(UI *ui, int count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
start = bot;
|
|
||||||
stop = top - count - 1;
|
|
||||||
step = -1;
|
|
||||||
if (data->can_use_terminal_scroll) {
|
if (data->can_use_terminal_scroll) {
|
||||||
if (count == -1) {
|
if (count == -1) {
|
||||||
unibi_out(ui, unibi_insert_line);
|
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[0].i = 0;
|
||||||
data->params[1].i = ui->height - 1;
|
data->params[1].i = ui->height - 1;
|
||||||
unibi_out(ui, unibi_change_scroll_region);
|
unibi_out(ui, unibi_change_scroll_region);
|
||||||
unibi_goto(ui, data->row, data->col);
|
unibi_goto(ui, grid->row, grid->col);
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
if (grid->bg != -1) {
|
||||||
// Scroll internal screen
|
// Update the cleared area of the terminal if its builtin scrolling
|
||||||
for (i = start; i != stop; i += step) {
|
// facility was used and the background color is not the default. This is
|
||||||
Cell *target_row = data->screen[i] + left;
|
// required because scrolling may leave wrong background in the cleared
|
||||||
Cell *source_row = data->screen[i + count] + left;
|
// area.
|
||||||
memcpy(target_row, source_row, sizeof(Cell) * (size_t)(right - left + 1));
|
clear_region(ui, clear_top, clear_bot, grid->left, grid->right);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
} else {
|
} 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
|
// Mark the entire scroll region as invalid for redrawing later
|
||||||
invalidate(ui, data->scroll_region.top, data->scroll_region.bot,
|
invalidate(ui, grid->top, grid->bot, grid->left, grid->right);
|
||||||
data->scroll_region.left, data->scroll_region.right);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_highlight_set(UI *ui, HlAttrs attrs)
|
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)
|
static void tui_put(UI *ui, uint8_t *text, size_t size)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
Cell *cell = data->screen[data->row] + data->col;
|
print_cell(ui, ugrid_put(&data->grid, text, size));
|
||||||
cell->data[size] = 0;
|
|
||||||
cell->attrs = data->attrs;
|
|
||||||
|
|
||||||
if (text) {
|
|
||||||
memcpy(cell->data, text, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_cell(ui, cell);
|
|
||||||
data->col += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_bell(UI *ui)
|
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)
|
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)
|
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)
|
static void tui_flush(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
|
UGrid *grid = &data->grid;
|
||||||
|
|
||||||
while (kv_size(data->invalid_regions)) {
|
while (kv_size(data->invalid_regions)) {
|
||||||
Rect r = kv_pop(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);
|
print_cell(ui, cell);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unibi_goto(ui, data->row, data->col);
|
unibi_goto(ui, grid->row, grid->col);
|
||||||
|
|
||||||
flush_buf(ui);
|
flush_buf(ui);
|
||||||
}
|
}
|
||||||
@@ -890,13 +826,3 @@ static void flush_buf(UI *ui)
|
|||||||
unibi_out(ui, unibi_cursor_invisible);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
137
src/nvim/ugrid.c
Normal file
137
src/nvim/ugrid.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
40
src/nvim/ugrid.h
Normal file
40
src/nvim/ugrid.h
Normal file
@@ -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
|
Reference in New Issue
Block a user