aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2022-05-08 13:04:18 +0200
committerbfredl <bjorn.linse@gmail.com>2022-05-08 13:22:09 +0200
commitdf41d884a7b788bb38060e732f1a7abb08de7b1b (patch)
treec7687eef00b35bdc02ba37437a70b7c37dda4026
parent0d3f17a6c317b26cdc319b48e25e1574f3a0e9fd (diff)
downloadrneovim-df41d884a7b788bb38060e732f1a7abb08de7b1b.tar.gz
rneovim-df41d884a7b788bb38060e732f1a7abb08de7b1b.tar.bz2
rneovim-df41d884a7b788bb38060e732f1a7abb08de7b1b.zip
refactor(grid): move out grid_* functions from screen.c
Also normalize some types. use "size_t" for unsigned array offsets. Fix -Wconversion issues missed as screen.c is missing this check.
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/grid.c708
-rw-r--r--src/nvim/grid.h57
-rw-r--r--src/nvim/grid_defs.h2
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/screen.c738
-rw-r--r--src/nvim/screen.h13
-rw-r--r--src/nvim/ui_compositor.c2
-rw-r--r--src/nvim/window.c4
10 files changed, 777 insertions, 752 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 1932c02cf2..d4cea15d20 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -3664,7 +3664,7 @@ static void cursorcmd(void)
static void cmd_cursor_goto(int row, int col)
{
ScreenGrid *grid = &msg_grid_adj;
- screen_adjust_grid(&grid, &row, &col);
+ grid_adjust(&grid, &row, &col);
ui_grid_cursor_goto(grid->handle, row, col);
}
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
new file mode 100644
index 0000000000..b150d3819e
--- /dev/null
+++ b/src/nvim/grid.c
@@ -0,0 +1,708 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/arabic.h"
+#include "nvim/highlight.h"
+#include "nvim/vim.h"
+#include "nvim/grid.h"
+#include "nvim/ui.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "grid.c.generated.h"
+#endif
+
+// temporary buffer for rendering a single screenline, so it can be
+// compared with previous contents to calculate smallest delta.
+// Per-cell attributes
+static size_t linebuf_size = 0;
+
+
+/// Determine if dedicated window grid should be used or the default_grid
+///
+/// If UI did not request multigrid support, draw all windows on the
+/// default_grid.
+///
+/// NB: this function can only been used with window grids in a context where
+/// win_grid_alloc already has been called!
+///
+/// If the default_grid is used, adjust window relative positions to global
+/// screen positions.
+void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
+{
+ if ((*grid)->target) {
+ *row_off += (*grid)->row_offset;
+ *col_off += (*grid)->col_offset;
+ *grid = (*grid)->target;
+ }
+}
+
+/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
+int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO])
+{
+ int len = utf_char2bytes(c, p);
+ for (int i = 0; i < MAX_MCO; i++) {
+ if (u8cc[i] == 0) {
+ break;
+ }
+ len += utf_char2bytes(u8cc[i], p + len);
+ }
+ p[len] = 0;
+ return len;
+}
+
+/// clear a line in the grid starting at "off" until "width" characters
+/// are cleared.
+void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid)
+{
+ for (int col = 0; col < width; col++) {
+ schar_from_ascii(grid->chars[off + (size_t)col], ' ');
+ }
+ int fill = valid ? 0 : -1;
+ (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
+}
+
+void grid_invalidate(ScreenGrid *grid)
+{
+ (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)(grid->Rows * grid->Columns));
+}
+
+bool grid_invalid_row(ScreenGrid *grid, int row)
+{
+ return grid->attrs[grid->line_offset[row]] < 0;
+}
+
+static int line_off2cells(schar_T *line, size_t off, size_t max_off)
+{
+ return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
+}
+
+/// Return number of display cells for char at grid->chars[off].
+/// We make sure that the offset used is less than "max_off".
+static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off)
+{
+ return line_off2cells(grid->chars, off, max_off);
+}
+
+/// Return true if the character at "row"/"col" on the screen is the left side
+/// of a double-width character.
+///
+/// Caller must make sure "row" and "col" are not invalid!
+bool grid_lefthalve(ScreenGrid *grid, int row, int col)
+{
+ grid_adjust(&grid, &row, &col);
+
+ return grid_off2cells(grid, grid->line_offset[row] + (size_t)col,
+ grid->line_offset[row] + (size_t)grid->Columns) > 1;
+}
+
+/// Correct a position on the screen, if it's the right half of a double-wide
+/// char move it to the left half. Returns the corrected column.
+int grid_fix_col(ScreenGrid *grid, int col, int row)
+{
+ int coloff = 0;
+ grid_adjust(&grid, &row, &coloff);
+
+ col += coloff;
+ if (grid->chars != NULL && col > 0
+ && grid->chars[grid->line_offset[row] + (size_t)col][0] == 0) {
+ return col - 1 - coloff;
+ }
+ return col - coloff;
+}
+
+/// output a single character directly to the grid
+void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
+{
+ char_u buf[MB_MAXBYTES + 1];
+
+ buf[utf_char2bytes(c, buf)] = NUL;
+ grid_puts(grid, buf, row, col, attr);
+}
+
+/// get a single character directly from grid.chars into "bytes[]".
+/// Also return its attribute in *attrp;
+void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp)
+{
+ size_t off;
+
+ grid_adjust(&grid, &row, &col);
+
+ // safety check
+ if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) {
+ off = grid->line_offset[row] + (size_t)col;
+ *attrp = grid->attrs[off];
+ schar_copy(bytes, grid->chars[off]);
+ }
+}
+
+
+/// put string '*text' on the window grid at position 'row' and 'col', with
+/// attributes 'attr', and update chars[] and attrs[].
+/// Note: only outputs within one row, message is truncated at grid boundary!
+/// Note: if grid, row and/or col is invalid, nothing is done.
+void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
+{
+ grid_puts_len(grid, text, -1, row, col, attr);
+}
+
+static ScreenGrid *put_dirty_grid = NULL;
+static int put_dirty_row = -1;
+static int put_dirty_first = INT_MAX;
+static int put_dirty_last = 0;
+
+/// Start a group of grid_puts_len calls that builds a single grid line.
+///
+/// Must be matched with a grid_puts_line_flush call before moving to
+/// another line.
+void grid_puts_line_start(ScreenGrid *grid, int row)
+{
+ int col = 0; // unused
+ grid_adjust(&grid, &row, &col);
+ assert(put_dirty_row == -1);
+ put_dirty_row = row;
+ put_dirty_grid = grid;
+}
+
+void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
+{
+ assert(put_dirty_row == row);
+ size_t off = grid->line_offset[row] + (size_t)col;
+ if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
+ schar_copy(grid->chars[off], schar);
+ grid->attrs[off] = attr;
+
+ put_dirty_first = MIN(put_dirty_first, col);
+ // TODO(bfredl): Y U NO DOUBLEWIDTH?
+ put_dirty_last = MAX(put_dirty_last, col + 1);
+ }
+}
+
+/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
+/// a NUL.
+void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr)
+{
+ size_t off;
+ char_u *ptr = text;
+ int len = textlen;
+ int c;
+ size_t max_off;
+ int mbyte_blen = 1;
+ int mbyte_cells = 1;
+ int u8c = 0;
+ int u8cc[MAX_MCO];
+ bool clear_next_cell = false;
+ int prev_c = 0; // previous Arabic character
+ int pc, nc, nc1;
+ int pcc[MAX_MCO];
+ int need_redraw;
+ bool do_flush = false;
+
+ grid_adjust(&grid, &row, &col);
+
+ // Safety check. The check for negative row and column is to fix issue
+ // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
+ if (grid->chars == NULL
+ || row >= grid->Rows || row < 0
+ || col >= grid->Columns || col < 0) {
+ return;
+ }
+
+ if (put_dirty_row == -1) {
+ grid_puts_line_start(grid, row);
+ do_flush = true;
+ } else {
+ if (grid != put_dirty_grid || row != put_dirty_row) {
+ abort();
+ }
+ }
+ off = grid->line_offset[row] + (size_t)col;
+
+ // When drawing over the right half of a double-wide char clear out the
+ // left half. Only needed in a terminal.
+ if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
+ // redraw the previous cell, make it empty
+ put_dirty_first = -1;
+ put_dirty_last = MAX(put_dirty_last, 1);
+ }
+
+ max_off = grid->line_offset[row] + (size_t)grid->Columns;
+ while (col < grid->Columns
+ && (len < 0 || (int)(ptr - text) < len)
+ && *ptr != NUL) {
+ c = *ptr;
+ // check if this is the first byte of a multibyte
+ if (len > 0) {
+ mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr));
+ } else {
+ mbyte_blen = utfc_ptr2len((char *)ptr);
+ }
+ if (len >= 0) {
+ u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr));
+ } else {
+ u8c = utfc_ptr2char(ptr, u8cc);
+ }
+ mbyte_cells = utf_char2cells(u8c);
+ if (p_arshape && !p_tbidi && arabic_char(u8c)) {
+ // Do Arabic shaping.
+ if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
+ // Past end of string to be displayed.
+ nc = NUL;
+ nc1 = NUL;
+ } else {
+ nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc,
+ (int)((text + len) - ptr - mbyte_blen));
+ nc1 = pcc[0];
+ }
+ pc = prev_c;
+ prev_c = u8c;
+ u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
+ } else {
+ prev_c = u8c;
+ }
+ if (col + mbyte_cells > grid->Columns) {
+ // Only 1 cell left, but character requires 2 cells:
+ // display a '>' in the last column to avoid wrapping. */
+ c = '>';
+ u8c = '>';
+ u8cc[0] = 0;
+ mbyte_cells = 1;
+ }
+
+ schar_T buf;
+ schar_from_cc(buf, u8c, u8cc);
+
+
+ need_redraw = schar_cmp(grid->chars[off], buf)
+ || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
+ || grid->attrs[off] != attr
+ || exmode_active;
+
+ if (need_redraw) {
+ // When at the end of the text and overwriting a two-cell
+ // character with a one-cell character, need to clear the next
+ // cell. Also when overwriting the left half of a two-cell char
+ // with the right half of a two-cell char. Do this only once
+ // (utf8_off2cells() may return 2 on the right half).
+ if (clear_next_cell) {
+ clear_next_cell = false;
+ } else if ((len < 0 ? ptr[mbyte_blen] == NUL : ptr + mbyte_blen >= text + len)
+ && ((mbyte_cells == 1
+ && grid_off2cells(grid, off, max_off) > 1)
+ || (mbyte_cells == 2
+ && grid_off2cells(grid, off, max_off) == 1
+ && grid_off2cells(grid, off + 1, max_off) > 1))) {
+ clear_next_cell = true;
+ }
+
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
+ grid->chars[off - 1][0] = '>';
+ grid->chars[off - 1][1] = 0;
+ }
+
+ schar_copy(grid->chars[off], buf);
+ grid->attrs[off] = attr;
+ if (mbyte_cells == 2) {
+ grid->chars[off + 1][0] = 0;
+ grid->attrs[off + 1] = attr;
+ }
+ put_dirty_first = MIN(put_dirty_first, col);
+ put_dirty_last = MAX(put_dirty_last, col + mbyte_cells);
+ }
+
+ off += (size_t)mbyte_cells;
+ col += mbyte_cells;
+ ptr += mbyte_blen;
+ if (clear_next_cell) {
+ // This only happens at the end, display one space next.
+ ptr = (char_u *)" ";
+ len = -1;
+ }
+ }
+
+ if (do_flush) {
+ grid_puts_line_flush(true);
+ }
+}
+
+/// End a group of grid_puts_len calls and send the screen buffer to the UI
+/// layer.
+///
+/// @param set_cursor Move the visible cursor to the end of the changed region.
+/// This is a workaround for not yet refactored code paths
+/// and shouldn't be used in new code.
+void grid_puts_line_flush(bool set_cursor)
+{
+ assert(put_dirty_row != -1);
+ if (put_dirty_first < put_dirty_last) {
+ if (set_cursor) {
+ ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
+ MIN(put_dirty_last, put_dirty_grid->Columns - 1));
+ }
+ if (!put_dirty_grid->throttled) {
+ ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
+ put_dirty_last, 0, false);
+ } else if (put_dirty_grid->dirty_col) {
+ if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
+ put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
+ }
+ }
+ put_dirty_first = INT_MAX;
+ put_dirty_last = 0;
+ }
+ put_dirty_row = -1;
+ put_dirty_grid = NULL;
+}
+
+/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
+/// to "end_col" (exclusive) with character "c1" in first column followed by
+/// "c2" in the other columns. Use attributes "attr".
+void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1,
+ int c2, int attr)
+{
+ schar_T sc;
+
+ int row_off = 0, col_off = 0;
+ grid_adjust(&grid, &row_off, &col_off);
+ start_row += row_off;
+ end_row += row_off;
+ start_col += col_off;
+ end_col += col_off;
+
+ // safety check
+ if (end_row > grid->Rows) {
+ end_row = grid->Rows;
+ }
+ if (end_col > grid->Columns) {
+ end_col = grid->Columns;
+ }
+
+ // nothing to do
+ if (start_row >= end_row || start_col >= end_col) {
+ return;
+ }
+
+ for (int row = start_row; row < end_row; row++) {
+ // When drawing over the right half of a double-wide char clear
+ // out the left half. When drawing over the left half of a
+ // double wide-char clear out the right half. Only needed in a
+ // terminal.
+ if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
+ grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
+ }
+ if (end_col < grid->Columns
+ && grid_fix_col(grid, end_col, row) != end_col) {
+ grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0);
+ }
+
+ // if grid was resized (in ext_multigrid mode), the UI has no redraw updates
+ // for the newly resized grid. It is better mark everything as dirty and
+ // send all the updates.
+ int dirty_first = INT_MAX;
+ int dirty_last = 0;
+
+ int col = start_col;
+ schar_from_char(sc, c1);
+ size_t lineoff = grid->line_offset[row];
+ for (col = start_col; col < end_col; col++) {
+ size_t off = lineoff + (size_t)col;
+ if (schar_cmp(grid->chars[off], sc)
+ || grid->attrs[off] != attr) {
+ schar_copy(grid->chars[off], sc);
+ grid->attrs[off] = attr;
+ if (dirty_first == INT_MAX) {
+ dirty_first = col;
+ }
+ dirty_last = col + 1;
+ }
+ if (col == start_col) {
+ schar_from_char(sc, c2);
+ }
+ }
+ if (dirty_last > dirty_first) {
+ // TODO(bfredl): support a cleared suffix even with a batched line?
+ if (put_dirty_row == row) {
+ put_dirty_first = MIN(put_dirty_first, dirty_first);
+ put_dirty_last = MAX(put_dirty_last, dirty_last);
+ } else if (grid->throttled) {
+ // Note: assumes msg_grid is the only throttled grid
+ assert(grid == &msg_grid);
+ int dirty = 0;
+ if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') {
+ dirty = dirty_last;
+ } else if (c1 != ' ') {
+ dirty = dirty_first + 1;
+ }
+ if (grid->dirty_col && dirty > grid->dirty_col[row]) {
+ grid->dirty_col[row] = dirty;
+ }
+ } else {
+ int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
+ ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
+ }
+ }
+
+ if (end_col == grid->Columns) {
+ grid->line_wraps[row] = false;
+ }
+ }
+}
+
+/// Check whether the given character needs redrawing:
+/// - the (first byte of the) character is different
+/// - the attributes are different
+/// - the character is multi-byte and the next byte is different
+/// - the character is two cells wide and the second cell differs.
+static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_to, int cols)
+{
+ return (cols > 0
+ && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to])
+ || linebuf_attr[off_from] != grid->attrs[off_to]
+ || (line_off2cells(linebuf_char, off_from, off_from + (size_t)cols) > 1
+ && schar_cmp(linebuf_char[off_from + 1],
+ grid->chars[off_to + 1])))
+ || rdb_flags & RDB_NODELTA));
+}
+
+/// Move one buffered line to the window grid, but only the characters that
+/// have actually changed. Handle insert/delete character.
+/// "coloff" gives the first column on the grid for this line.
+/// "endcol" gives the columns where valid characters are.
+/// "clear_width" is the width of the window. It's > 0 if the rest of the line
+/// needs to be cleared, negative otherwise.
+/// "rlflag" is TRUE in a rightleft window:
+/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
+/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
+/// If "wrap" is true, then hint to the UI that "row" contains a line
+/// which has wrapped into the next row.
+void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
+ int rlflag, win_T *wp, int bg_attr, bool wrap)
+{
+ size_t max_off_from;
+ size_t max_off_to;
+ int col = 0;
+ bool redraw_this; // Does character need redraw?
+ bool redraw_next; // redraw_this for next character
+ bool clear_next = false;
+ int char_cells; // 1: normal char
+ // 2: occupies two display cells
+ int start_dirty = -1, end_dirty = 0;
+
+ // TODO(bfredl): check all callsites and eliminate
+ // Check for illegal row and col, just in case
+ if (row >= grid->Rows) {
+ row = grid->Rows - 1;
+ }
+ if (endcol > grid->Columns) {
+ endcol = grid->Columns;
+ }
+
+ grid_adjust(&grid, &row, &coloff);
+
+ // Safety check. Avoids clang warnings down the call stack.
+ if (grid->chars == NULL || row >= grid->Rows || coloff >= grid->Columns) {
+ DLOG("invalid state, skipped");
+ return;
+ }
+
+ size_t off_from = 0;
+ size_t off_to = grid->line_offset[row] + (size_t)coloff;
+ max_off_from = linebuf_size;
+ max_off_to = grid->line_offset[row] + (size_t)grid->Columns;
+
+ if (rlflag) {
+ // Clear rest first, because it's left of the text.
+ if (clear_width > 0) {
+ while (col <= endcol && grid->chars[off_to][0] == ' '
+ && grid->chars[off_to][1] == NUL
+ && grid->attrs[off_to] == bg_attr) {
+ off_to++;
+ col++;
+ }
+ if (col <= endcol) {
+ grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', bg_attr);
+ }
+ }
+ col = endcol + 1;
+ off_to = grid->line_offset[row] + (size_t)col + (size_t)coloff;
+ off_from += (size_t)col;
+ endcol = (clear_width > 0 ? clear_width : -clear_width);
+ }
+
+ if (bg_attr) {
+ for (int c = col; c < endcol; c++) {
+ linebuf_attr[off_from + (size_t)c] =
+ hl_combine_attr(bg_attr, linebuf_attr[off_from + (size_t)c]);
+ }
+ }
+
+ redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col);
+
+ while (col < endcol) {
+ char_cells = 1;
+ if (col + 1 < endcol) {
+ char_cells = line_off2cells(linebuf_char, off_from, max_off_from);
+ }
+ redraw_this = redraw_next;
+ redraw_next = grid_char_needs_redraw(grid, off_from + (size_t)char_cells,
+ off_to + (size_t)char_cells,
+ endcol - col - char_cells);
+
+ if (redraw_this) {
+ if (start_dirty == -1) {
+ start_dirty = col;
+ }
+ end_dirty = col + char_cells;
+ // When writing a single-width character over a double-width
+ // character and at the end of the redrawn text, need to clear out
+ // the right half of the old character.
+ // Also required when writing the right half of a double-width
+ // char over the left half of an existing one
+ if (col + char_cells == endcol
+ && ((char_cells == 1
+ && grid_off2cells(grid, off_to, max_off_to) > 1)
+ || (char_cells == 2
+ && grid_off2cells(grid, off_to, max_off_to) == 1
+ && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) {
+ clear_next = true;
+ }
+
+ schar_copy(grid->chars[off_to], linebuf_char[off_from]);
+ if (char_cells == 2) {
+ schar_copy(grid->chars[off_to + 1], linebuf_char[off_from + 1]);
+ }
+
+ grid->attrs[off_to] = linebuf_attr[off_from];
+ // For simplicity set the attributes of second half of a
+ // double-wide character equal to the first half.
+ if (char_cells == 2) {
+ grid->attrs[off_to + 1] = linebuf_attr[off_from];
+ }
+ }
+
+ off_to += (size_t)char_cells;
+ off_from += (size_t)char_cells;
+ col += char_cells;
+ }
+
+ if (clear_next) {
+ // Clear the second half of a double-wide character of which the left
+ // half was overwritten with a single-wide character.
+ schar_from_ascii(grid->chars[off_to], ' ');
+ end_dirty++;
+ }
+
+ int clear_end = -1;
+ if (clear_width > 0 && !rlflag) {
+ // blank out the rest of the line
+ // TODO(bfredl): we could cache winline widths
+ while (col < clear_width) {
+ if (grid->chars[off_to][0] != ' '
+ || grid->chars[off_to][1] != NUL
+ || grid->attrs[off_to] != bg_attr) {
+ grid->chars[off_to][0] = ' ';
+ grid->chars[off_to][1] = NUL;
+ grid->attrs[off_to] = bg_attr;
+ if (start_dirty == -1) {
+ start_dirty = col;
+ end_dirty = col;
+ } else if (clear_end == -1) {
+ end_dirty = endcol;
+ }
+ clear_end = col + 1;
+ }
+ col++;
+ off_to++;
+ }
+ }
+
+ if (clear_width > 0 || wp->w_width != grid->Columns) {
+ // If we cleared after the end of the line, it did not wrap.
+ // For vsplit, line wrapping is not possible.
+ grid->line_wraps[row] = false;
+ }
+
+ if (clear_end < end_dirty) {
+ clear_end = end_dirty;
+ }
+ if (start_dirty == -1) {
+ start_dirty = end_dirty;
+ }
+ if (clear_end > start_dirty) {
+ ui_line(grid, row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end,
+ bg_attr, wrap);
+ }
+}
+
+void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
+{
+ int new_row;
+ ScreenGrid new = *grid;
+ assert(rows >= 0 && columns >= 0);
+ size_t ncells = (size_t)rows * (size_t)columns;
+ new.chars = xmalloc(ncells * sizeof(schar_T));
+ new.attrs = xmalloc(ncells * sizeof(sattr_T));
+ new.line_offset = xmalloc((size_t)rows * sizeof(*new.line_offset));
+ new.line_wraps = xmalloc((size_t)rows * sizeof(*new.line_wraps));
+
+ new.Rows = rows;
+ new.Columns = columns;
+
+ for (new_row = 0; new_row < new.Rows; new_row++) {
+ new.line_offset[new_row] = (size_t)new_row * (size_t)new.Columns;
+ new.line_wraps[new_row] = false;
+
+ grid_clear_line(&new, new.line_offset[new_row], columns, valid);
+
+ if (copy) {
+ // If the screen is not going to be cleared, copy as much as
+ // possible from the old screen to the new one and clear the rest
+ // (used when resizing the window at the "--more--" prompt or when
+ // executing an external command, for the GUI).
+ if (new_row < grid->Rows && grid->chars != NULL) {
+ int len = MIN(grid->Columns, new.Columns);
+ memmove(new.chars + new.line_offset[new_row],
+ grid->chars + grid->line_offset[new_row],
+ (size_t)len * sizeof(schar_T));
+ memmove(new.attrs + new.line_offset[new_row],
+ grid->attrs + grid->line_offset[new_row],
+ (size_t)len * sizeof(sattr_T));
+ }
+ }
+ }
+ grid_free(grid);
+ *grid = new;
+
+ // Share a single scratch buffer for all grids, by
+ // ensuring it is as wide as the widest grid.
+ if (linebuf_size < (size_t)columns) {
+ xfree(linebuf_char);
+ xfree(linebuf_attr);
+ linebuf_char = xmalloc((size_t)columns * sizeof(schar_T));
+ linebuf_attr = xmalloc((size_t)columns * sizeof(sattr_T));
+ linebuf_size = (size_t)columns;
+ }
+}
+
+void grid_free(ScreenGrid *grid)
+{
+ xfree(grid->chars);
+ xfree(grid->attrs);
+ xfree(grid->line_offset);
+ xfree(grid->line_wraps);
+
+ grid->chars = NULL;
+ grid->attrs = NULL;
+ grid->line_offset = NULL;
+ grid->line_wraps = NULL;
+}
+
+/// Doesn't allow reinit, so must only be called by free_all_mem!
+void grid_free_all_mem(void)
+{
+ grid_free(&default_grid);
+ xfree(linebuf_char);
+ xfree(linebuf_attr);
+}
+
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
new file mode 100644
index 0000000000..12a5bf95bb
--- /dev/null
+++ b/src/nvim/grid.h
@@ -0,0 +1,57 @@
+#ifndef NVIM_GRID_H
+#define NVIM_GRID_H
+
+#include <stdbool.h>
+
+#include "nvim/ascii.h"
+#include "nvim/grid_defs.h"
+#include "nvim/buffer_defs.h"
+
+/// By default, all windows are drawn on a single rectangular grid, represented by
+/// this ScreenGrid instance. In multigrid mode each window will have its own
+/// grid, then this is only used for global screen elements that hasn't been
+/// externalized.
+///
+/// Note: before the screen is initialized and when out of memory these can be
+/// NULL.
+EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
+
+#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
+
+EXTERN schar_T *linebuf_char INIT(= NULL);
+EXTERN sattr_T *linebuf_attr INIT(= NULL);
+
+// Low-level functions to manipulate individual character cells on the
+// screen grid.
+
+/// Put a ASCII character in a screen cell.
+static inline void schar_from_ascii(char_u *p, const char c)
+{
+ p[0] = (char_u)c;
+ p[1] = 0;
+}
+
+/// Put a unicode character in a screen cell.
+static inline int schar_from_char(char_u *p, int c)
+{
+ int len = utf_char2bytes(c, p);
+ p[len] = NUL;
+ return len;
+}
+
+/// compare the contents of two screen cells.
+static inline int schar_cmp(char_u *sc1, char_u *sc2)
+{
+ return strncmp((char *)sc1, (char *)sc2, sizeof(schar_T));
+}
+
+/// copy the contents of screen cell `sc2` into cell `sc1`
+static inline void schar_copy(char_u *sc1, char_u *sc2)
+{
+ xstrlcpy((char *)sc1, (char *)sc2, sizeof(schar_T));
+}
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "grid.h.generated.h"
+#endif
+#endif // NVIM_GRID_H
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 69eca5d123..2516ea52a7 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -50,7 +50,7 @@ struct ScreenGrid {
schar_T *chars;
sattr_T *attrs;
- unsigned *line_offset;
+ size_t *line_offset;
char_u *line_wraps;
// last column that was drawn (not cleared with the default background).
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 3f9e875d74..9beaa0686f 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -37,6 +37,7 @@
# include <locale.h>
#endif
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/log.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 19144a5dce..8a65188f22 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -696,7 +696,7 @@ void free_all_mem(void)
}
// free screenlines (can't display anything now!)
- screen_free_all_mem();
+ grid_free_all_mem();
clear_hl_tables(false);
list_free_log();
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index f3e627cc65..968397fbfa 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -130,13 +130,6 @@
#define MB_FILLER_CHAR '<' /* character used when a double-width character
* doesn't fit. */
-// temporary buffer for rendering a single screenline, so it can be
-// compared with previous contents to calculate smallest delta.
-// Per-cell attributes
-static size_t linebuf_size = 0;
-static schar_T *linebuf_char = NULL;
-static sattr_T *linebuf_attr = NULL;
-
static match_T search_hl; // used for 'hlsearch' highlight matching
StlClickDefinition *tab_page_click_defs = NULL;
@@ -4221,7 +4214,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (wrap) {
ScreenGrid *current_grid = grid;
int current_row = row, dummy_col = 0; // dummy_col unused
- screen_adjust_grid(&current_grid, &current_row, &dummy_col);
+ grid_adjust(&current_grid, &current_row, &dummy_col);
// Force a redraw of the first column of the next line.
current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
@@ -4387,25 +4380,6 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
return col;
}
-/// Determine if dedicated window grid should be used or the default_grid
-///
-/// If UI did not request multigrid support, draw all windows on the
-/// default_grid.
-///
-/// NB: this function can only been used with window grids in a context where
-/// win_grid_alloc already has been called!
-///
-/// If the default_grid is used, adjust window relative positions to global
-/// screen positions.
-void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
-{
- if ((*grid)->target) {
- *row_off += (*grid)->row_offset;
- *col_off += (*grid)->col_offset;
- *grid = (*grid)->target;
- }
-}
-
// Return true if CursorLineSign highlight is to be used.
static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
{
@@ -4535,196 +4509,6 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att
}
-/*
- * Check whether the given character needs redrawing:
- * - the (first byte of the) character is different
- * - the attributes are different
- * - the character is multi-byte and the next byte is different
- * - the character is two cells wide and the second cell differs.
- */
-static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, int cols)
-{
- return (cols > 0
- && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to])
- || linebuf_attr[off_from] != grid->attrs[off_to]
- || (line_off2cells(linebuf_char, off_from, off_from + cols) > 1
- && schar_cmp(linebuf_char[off_from + 1],
- grid->chars[off_to + 1])))
- || rdb_flags & RDB_NODELTA));
-}
-
-/// Move one buffered line to the window grid, but only the characters that
-/// have actually changed. Handle insert/delete character.
-/// "coloff" gives the first column on the grid for this line.
-/// "endcol" gives the columns where valid characters are.
-/// "clear_width" is the width of the window. It's > 0 if the rest of the line
-/// needs to be cleared, negative otherwise.
-/// "rlflag" is TRUE in a rightleft window:
-/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
-/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
-/// If "wrap" is true, then hint to the UI that "row" contains a line
-/// which has wrapped into the next row.
-static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
- int rlflag, win_T *wp, int bg_attr, bool wrap)
-{
- unsigned off_from;
- unsigned off_to;
- unsigned max_off_from;
- unsigned max_off_to;
- int col = 0;
- bool redraw_this; // Does character need redraw?
- bool redraw_next; // redraw_this for next character
- bool clear_next = false;
- int char_cells; // 1: normal char
- // 2: occupies two display cells
- int start_dirty = -1, end_dirty = 0;
-
- // TODO(bfredl): check all callsites and eliminate
- // Check for illegal row and col, just in case
- if (row >= grid->Rows) {
- row = grid->Rows - 1;
- }
- if (endcol > grid->Columns) {
- endcol = grid->Columns;
- }
-
- screen_adjust_grid(&grid, &row, &coloff);
-
- // Safety check. Avoids clang warnings down the call stack.
- if (grid->chars == NULL || row >= grid->Rows || coloff >= grid->Columns) {
- DLOG("invalid state, skipped");
- return;
- }
-
- off_from = 0;
- off_to = grid->line_offset[row] + coloff;
- max_off_from = linebuf_size;
- max_off_to = grid->line_offset[row] + grid->Columns;
-
- if (rlflag) {
- // Clear rest first, because it's left of the text.
- if (clear_width > 0) {
- while (col <= endcol && grid->chars[off_to][0] == ' '
- && grid->chars[off_to][1] == NUL
- && grid->attrs[off_to] == bg_attr) {
- ++off_to;
- ++col;
- }
- if (col <= endcol) {
- grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1,
- ' ', ' ', bg_attr);
- }
- }
- col = endcol + 1;
- off_to = grid->line_offset[row] + col + coloff;
- off_from += col;
- endcol = (clear_width > 0 ? clear_width : -clear_width);
- }
-
- if (bg_attr) {
- for (int c = col; c < endcol; c++) {
- linebuf_attr[off_from + c] =
- hl_combine_attr(bg_attr, linebuf_attr[off_from + c]);
- }
- }
-
- redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col);
-
- while (col < endcol) {
- char_cells = 1;
- if (col + 1 < endcol) {
- char_cells = line_off2cells(linebuf_char, off_from, max_off_from);
- }
- redraw_this = redraw_next;
- redraw_next = grid_char_needs_redraw(grid, off_from + char_cells,
- off_to + char_cells,
- endcol - col - char_cells);
-
- if (redraw_this) {
- if (start_dirty == -1) {
- start_dirty = col;
- }
- end_dirty = col + char_cells;
- // When writing a single-width character over a double-width
- // character and at the end of the redrawn text, need to clear out
- // the right half of the old character.
- // Also required when writing the right half of a double-width
- // char over the left half of an existing one
- if (col + char_cells == endcol
- && ((char_cells == 1
- && grid_off2cells(grid, off_to, max_off_to) > 1)
- || (char_cells == 2
- && grid_off2cells(grid, off_to, max_off_to) == 1
- && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) {
- clear_next = true;
- }
-
- schar_copy(grid->chars[off_to], linebuf_char[off_from]);
- if (char_cells == 2) {
- schar_copy(grid->chars[off_to + 1], linebuf_char[off_from + 1]);
- }
-
- grid->attrs[off_to] = linebuf_attr[off_from];
- // For simplicity set the attributes of second half of a
- // double-wide character equal to the first half.
- if (char_cells == 2) {
- grid->attrs[off_to + 1] = linebuf_attr[off_from];
- }
- }
-
- off_to += char_cells;
- off_from += char_cells;
- col += char_cells;
- }
-
- if (clear_next) {
- // Clear the second half of a double-wide character of which the left
- // half was overwritten with a single-wide character.
- schar_from_ascii(grid->chars[off_to], ' ');
- end_dirty++;
- }
-
- int clear_end = -1;
- if (clear_width > 0 && !rlflag) {
- // blank out the rest of the line
- // TODO(bfredl): we could cache winline widths
- while (col < clear_width) {
- if (grid->chars[off_to][0] != ' '
- || grid->chars[off_to][1] != NUL
- || grid->attrs[off_to] != bg_attr) {
- grid->chars[off_to][0] = ' ';
- grid->chars[off_to][1] = NUL;
- grid->attrs[off_to] = bg_attr;
- if (start_dirty == -1) {
- start_dirty = col;
- end_dirty = col;
- } else if (clear_end == -1) {
- end_dirty = endcol;
- }
- clear_end = col + 1;
- }
- col++;
- off_to++;
- }
- }
-
- if (clear_width > 0 || wp->w_width != grid->Columns) {
- // If we cleared after the end of the line, it did not wrap.
- // For vsplit, line wrapping is not possible.
- grid->line_wraps[row] = false;
- }
-
- if (clear_end < end_dirty) {
- clear_end = end_dirty;
- }
- if (start_dirty == -1) {
- start_dirty = end_dirty;
- }
- if (clear_end > start_dirty) {
- ui_line(grid, row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end,
- bg_attr, wrap);
- }
-}
/*
* Mirror text "str" for right-left displaying.
@@ -5676,334 +5460,6 @@ static void win_redr_border(win_T *wp)
}
}
-// Low-level functions to manipulate individual character cells on the
-// screen grid.
-
-/// Put a ASCII character in a screen cell.
-static void schar_from_ascii(char_u *p, const char c)
-{
- p[0] = c;
- p[1] = 0;
-}
-
-/// Put a unicode character in a screen cell.
-static int schar_from_char(char_u *p, int c)
-{
- int len = utf_char2bytes(c, p);
- p[len] = NUL;
- return len;
-}
-
-/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
-static int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO])
-{
- int len = utf_char2bytes(c, p);
- for (int i = 0; i < MAX_MCO; i++) {
- if (u8cc[i] == 0) {
- break;
- }
- len += utf_char2bytes(u8cc[i], p + len);
- }
- p[len] = 0;
- return len;
-}
-
-/// compare the contents of two screen cells.
-static int schar_cmp(char_u *sc1, char_u *sc2)
-{
- return STRNCMP(sc1, sc2, sizeof(schar_T));
-}
-
-/// copy the contents of screen cell `sc2` into cell `sc1`
-static void schar_copy(char_u *sc1, char_u *sc2)
-{
- STRLCPY(sc1, sc2, sizeof(schar_T));
-}
-
-static int line_off2cells(schar_T *line, size_t off, size_t max_off)
-{
- return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
-}
-
-/// Return number of display cells for char at grid->chars[off].
-/// We make sure that the offset used is less than "max_off".
-static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off)
-{
- return line_off2cells(grid->chars, off, max_off);
-}
-
-/// Return true if the character at "row"/"col" on the screen is the left side
-/// of a double-width character.
-///
-/// Caller must make sure "row" and "col" are not invalid!
-bool grid_lefthalve(ScreenGrid *grid, int row, int col)
-{
- screen_adjust_grid(&grid, &row, &col);
-
- return grid_off2cells(grid, grid->line_offset[row] + col,
- grid->line_offset[row] + grid->Columns) > 1;
-}
-
-/// Correct a position on the screen, if it's the right half of a double-wide
-/// char move it to the left half. Returns the corrected column.
-int grid_fix_col(ScreenGrid *grid, int col, int row)
-{
- int coloff = 0;
- screen_adjust_grid(&grid, &row, &coloff);
-
- col += coloff;
- if (grid->chars != NULL && col > 0
- && grid->chars[grid->line_offset[row] + col][0] == 0) {
- return col - 1 - coloff;
- }
- return col - coloff;
-}
-
-/// output a single character directly to the grid
-void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
-{
- char_u buf[MB_MAXBYTES + 1];
-
- buf[utf_char2bytes(c, buf)] = NUL;
- grid_puts(grid, buf, row, col, attr);
-}
-
-/// get a single character directly from grid.chars into "bytes[]".
-/// Also return its attribute in *attrp;
-void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp)
-{
- unsigned off;
-
- screen_adjust_grid(&grid, &row, &col);
-
- // safety check
- if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) {
- off = grid->line_offset[row] + col;
- *attrp = grid->attrs[off];
- schar_copy(bytes, grid->chars[off]);
- }
-}
-
-
-/// put string '*text' on the window grid at position 'row' and 'col', with
-/// attributes 'attr', and update chars[] and attrs[].
-/// Note: only outputs within one row, message is truncated at grid boundary!
-/// Note: if grid, row and/or col is invalid, nothing is done.
-void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
-{
- grid_puts_len(grid, text, -1, row, col, attr);
-}
-
-static ScreenGrid *put_dirty_grid = NULL;
-static int put_dirty_row = -1;
-static int put_dirty_first = INT_MAX;
-static int put_dirty_last = 0;
-
-/// Start a group of grid_puts_len calls that builds a single grid line.
-///
-/// Must be matched with a grid_puts_line_flush call before moving to
-/// another line.
-void grid_puts_line_start(ScreenGrid *grid, int row)
-{
- int col = 0; // unused
- screen_adjust_grid(&grid, &row, &col);
- assert(put_dirty_row == -1);
- put_dirty_row = row;
- put_dirty_grid = grid;
-}
-
-void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
-{
- assert(put_dirty_row == row);
- unsigned int off = grid->line_offset[row] + col;
- if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
- schar_copy(grid->chars[off], schar);
- grid->attrs[off] = attr;
-
- put_dirty_first = MIN(put_dirty_first, col);
- // TODO(bfredl): Y U NO DOUBLEWIDTH?
- put_dirty_last = MAX(put_dirty_last, col + 1);
- }
-}
-
-/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
-/// a NUL.
-void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr)
-{
- unsigned off;
- char_u *ptr = text;
- int len = textlen;
- int c;
- unsigned max_off;
- int mbyte_blen = 1;
- int mbyte_cells = 1;
- int u8c = 0;
- int u8cc[MAX_MCO];
- int clear_next_cell = FALSE;
- int prev_c = 0; // previous Arabic character
- int pc, nc, nc1;
- int pcc[MAX_MCO];
- int need_redraw;
- bool do_flush = false;
-
- screen_adjust_grid(&grid, &row, &col);
-
- // Safety check. The check for negative row and column is to fix issue
- // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
- if (grid->chars == NULL
- || row >= grid->Rows || row < 0
- || col >= grid->Columns || col < 0) {
- return;
- }
-
- if (put_dirty_row == -1) {
- grid_puts_line_start(grid, row);
- do_flush = true;
- } else {
- if (grid != put_dirty_grid || row != put_dirty_row) {
- abort();
- }
- }
- off = grid->line_offset[row] + col;
-
- // When drawing over the right half of a double-wide char clear out the
- // left half. Only needed in a terminal.
- if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
- // redraw the previous cell, make it empty
- put_dirty_first = -1;
- put_dirty_last = MAX(put_dirty_last, 1);
- }
-
- max_off = grid->line_offset[row] + grid->Columns;
- while (col < grid->Columns
- && (len < 0 || (int)(ptr - text) < len)
- && *ptr != NUL) {
- c = *ptr;
- // check if this is the first byte of a multibyte
- if (len > 0) {
- mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr));
- } else {
- mbyte_blen = utfc_ptr2len((char *)ptr);
- }
- if (len >= 0) {
- u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr));
- } else {
- u8c = utfc_ptr2char(ptr, u8cc);
- }
- mbyte_cells = utf_char2cells(u8c);
- if (p_arshape && !p_tbidi && arabic_char(u8c)) {
- // Do Arabic shaping.
- if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
- // Past end of string to be displayed.
- nc = NUL;
- nc1 = NUL;
- } else {
- nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc,
- (int)((text + len) - ptr - mbyte_blen));
- nc1 = pcc[0];
- }
- pc = prev_c;
- prev_c = u8c;
- u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
- } else {
- prev_c = u8c;
- }
- if (col + mbyte_cells > grid->Columns) {
- // Only 1 cell left, but character requires 2 cells:
- // display a '>' in the last column to avoid wrapping. */
- c = '>';
- u8c = '>';
- u8cc[0] = 0;
- mbyte_cells = 1;
- }
-
- schar_T buf;
- schar_from_cc(buf, u8c, u8cc);
-
-
- need_redraw = schar_cmp(grid->chars[off], buf)
- || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
- || grid->attrs[off] != attr
- || exmode_active;
-
- if (need_redraw) {
- // When at the end of the text and overwriting a two-cell
- // character with a one-cell character, need to clear the next
- // cell. Also when overwriting the left half of a two-cell char
- // with the right half of a two-cell char. Do this only once
- // (utf8_off2cells() may return 2 on the right half).
- if (clear_next_cell) {
- clear_next_cell = false;
- } else if ((len < 0 ? ptr[mbyte_blen] == NUL
- : ptr + mbyte_blen >= text + len)
- && ((mbyte_cells == 1
- && grid_off2cells(grid, off, max_off) > 1)
- || (mbyte_cells == 2
- && grid_off2cells(grid, off, max_off) == 1
- && grid_off2cells(grid, off + 1, max_off) > 1))) {
- clear_next_cell = true;
- }
-
- // When at the start of the text and overwriting the right half of a
- // two-cell character in the same grid, truncate that into a '>'.
- if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
- grid->chars[off - 1][0] = '>';
- grid->chars[off - 1][1] = 0;
- }
-
- schar_copy(grid->chars[off], buf);
- grid->attrs[off] = attr;
- if (mbyte_cells == 2) {
- grid->chars[off + 1][0] = 0;
- grid->attrs[off + 1] = attr;
- }
- put_dirty_first = MIN(put_dirty_first, col);
- put_dirty_last = MAX(put_dirty_last, col + mbyte_cells);
- }
-
- off += mbyte_cells;
- col += mbyte_cells;
- ptr += mbyte_blen;
- if (clear_next_cell) {
- // This only happens at the end, display one space next.
- ptr = (char_u *)" ";
- len = -1;
- }
- }
-
- if (do_flush) {
- grid_puts_line_flush(true);
- }
-}
-
-/// End a group of grid_puts_len calls and send the screen buffer to the UI
-/// layer.
-///
-/// @param set_cursor Move the visible cursor to the end of the changed region.
-/// This is a workaround for not yet refactored code paths
-/// and shouldn't be used in new code.
-void grid_puts_line_flush(bool set_cursor)
-{
- assert(put_dirty_row != -1);
- if (put_dirty_first < put_dirty_last) {
- if (set_cursor) {
- ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
- MIN(put_dirty_last, put_dirty_grid->Columns - 1));
- }
- if (!put_dirty_grid->throttled) {
- ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
- put_dirty_last, 0, false);
- } else if (put_dirty_grid->dirty_col) {
- if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
- put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
- }
- }
- put_dirty_first = INT_MAX;
- put_dirty_last = 0;
- }
- put_dirty_row = -1;
- put_dirty_grid = NULL;
-}
/*
* Prepare for 'hlsearch' highlighting.
@@ -6030,99 +5486,6 @@ static void end_search_hl(void)
}
-/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
-/// to "end_col" (exclusive) with character "c1" in first column followed by
-/// "c2" in the other columns. Use attributes "attr".
-void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1,
- int c2, int attr)
-{
- schar_T sc;
-
- int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
- start_row += row_off;
- end_row += row_off;
- start_col += col_off;
- end_col += col_off;
-
- // safety check
- if (end_row > grid->Rows) {
- end_row = grid->Rows;
- }
- if (end_col > grid->Columns) {
- end_col = grid->Columns;
- }
-
- // nothing to do
- if (start_row >= end_row || start_col >= end_col) {
- return;
- }
-
- for (int row = start_row; row < end_row; row++) {
- // When drawing over the right half of a double-wide char clear
- // out the left half. When drawing over the left half of a
- // double wide-char clear out the right half. Only needed in a
- // terminal.
- if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
- }
- if (end_col < grid->Columns
- && grid_fix_col(grid, end_col, row) != end_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0);
- }
-
- // if grid was resized (in ext_multigrid mode), the UI has no redraw updates
- // for the newly resized grid. It is better mark everything as dirty and
- // send all the updates.
- int dirty_first = INT_MAX;
- int dirty_last = 0;
-
- int col = start_col;
- schar_from_char(sc, c1);
- int lineoff = grid->line_offset[row];
- for (col = start_col; col < end_col; col++) {
- int off = lineoff + col;
- if (schar_cmp(grid->chars[off], sc)
- || grid->attrs[off] != attr) {
- schar_copy(grid->chars[off], sc);
- grid->attrs[off] = attr;
- if (dirty_first == INT_MAX) {
- dirty_first = col;
- }
- dirty_last = col + 1;
- }
- if (col == start_col) {
- schar_from_char(sc, c2);
- }
- }
- if (dirty_last > dirty_first) {
- // TODO(bfredl): support a cleared suffix even with a batched line?
- if (put_dirty_row == row) {
- put_dirty_first = MIN(put_dirty_first, dirty_first);
- put_dirty_last = MAX(put_dirty_last, dirty_last);
- } else if (grid->throttled) {
- // Note: assumes msg_grid is the only throttled grid
- assert(grid == &msg_grid);
- int dirty = 0;
- if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') {
- dirty = dirty_last;
- } else if (c1 != ' ') {
- dirty = dirty_first + 1;
- }
- if (grid->dirty_col && dirty > grid->dirty_col[row]) {
- grid->dirty_col[row] = dirty;
- }
- } else {
- int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
- ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
- }
- }
-
- if (end_col == grid->Columns) {
- grid->line_wraps[row] = false;
- }
- }
-}
/// Check if there should be a delay. Used before clearing or redrawing the
/// screen or the command line.
@@ -6319,77 +5682,6 @@ retry:
resizing = false;
}
-void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
-{
- int new_row;
- ScreenGrid new = *grid;
- assert(rows >= 0 && columns >= 0);
- size_t ncells = (size_t)rows * columns;
- new.chars = xmalloc(ncells * sizeof(schar_T));
- new.attrs = xmalloc(ncells * sizeof(sattr_T));
- new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned)));
- new.line_wraps = xmalloc((size_t)(rows * sizeof(char_u)));
-
- new.Rows = rows;
- new.Columns = columns;
-
- for (new_row = 0; new_row < new.Rows; new_row++) {
- new.line_offset[new_row] = new_row * new.Columns;
- new.line_wraps[new_row] = false;
-
- grid_clear_line(&new, new.line_offset[new_row], columns, valid);
-
- if (copy) {
- // If the screen is not going to be cleared, copy as much as
- // possible from the old screen to the new one and clear the rest
- // (used when resizing the window at the "--more--" prompt or when
- // executing an external command, for the GUI).
- if (new_row < grid->Rows && grid->chars != NULL) {
- int len = MIN(grid->Columns, new.Columns);
- memmove(new.chars + new.line_offset[new_row],
- grid->chars + grid->line_offset[new_row],
- (size_t)len * sizeof(schar_T));
- memmove(new.attrs + new.line_offset[new_row],
- grid->attrs + grid->line_offset[new_row],
- (size_t)len * sizeof(sattr_T));
- }
- }
- }
- grid_free(grid);
- *grid = new;
-
- // Share a single scratch buffer for all grids, by
- // ensuring it is as wide as the widest grid.
- if (linebuf_size < (size_t)columns) {
- xfree(linebuf_char);
- xfree(linebuf_attr);
- linebuf_char = xmalloc(columns * sizeof(schar_T));
- linebuf_attr = xmalloc(columns * sizeof(sattr_T));
- linebuf_size = columns;
- }
-}
-
-void grid_free(ScreenGrid *grid)
-{
- xfree(grid->chars);
- xfree(grid->attrs);
- xfree(grid->line_offset);
- xfree(grid->line_wraps);
-
- grid->chars = NULL;
- grid->attrs = NULL;
- grid->line_offset = NULL;
- grid->line_wraps = NULL;
-}
-
-/// Doesn't allow reinit, so must only be called by free_all_mem!
-void screen_free_all_mem(void)
-{
- grid_free(&default_grid);
- xfree(linebuf_char);
- xfree(linebuf_attr);
-}
-
/// Clear tab_page_click_defs table
///
/// @param[out] tpcd Table to clear.
@@ -6457,28 +5749,6 @@ void screenclear(void)
}
}
-/// clear a line in the grid starting at "off" until "width" characters
-/// are cleared.
-void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid)
-{
- for (int col = 0; col < width; col++) {
- schar_from_ascii(grid->chars[off + col], ' ');
- }
- int fill = valid ? 0 : -1;
- (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
-}
-
-void grid_invalidate(ScreenGrid *grid)
-{
- (void)memset(grid->attrs, -1, sizeof(sattr_T) * grid->Rows * grid->Columns);
-}
-
-bool grid_invalid_row(ScreenGrid *grid, int row)
-{
- return grid->attrs[grid->line_offset[row]] < 0;
-}
-
-
/// Copy part of a grid line for vertically split window.
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
{
@@ -6510,7 +5780,7 @@ void setcursor(void)
&& vim_isprintc(gchar_cursor())) ? 2 : 1);
}
- screen_adjust_grid(&grid, &row, &col);
+ grid_adjust(&grid, &row, &col);
ui_grid_cursor_goto(grid->handle, row, col);
}
}
@@ -6563,7 +5833,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
unsigned temp;
int row_off = 0;
- screen_adjust_grid(&grid, &row_off, &col);
+ grid_adjust(&grid, &row_off, &col);
row += row_off;
end += row_off;
@@ -6612,7 +5882,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
unsigned temp;
int row_off = 0;
- screen_adjust_grid(&grid, &row_off, &col);
+ grid_adjust(&grid, &row_off, &col);
row += row_off;
end += row_off;
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 33b713e756..3afbaa5eb6 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -4,7 +4,7 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/pos.h"
#include "nvim/types.h"
@@ -27,17 +27,6 @@ typedef enum {
WC_BOTTOM_RIGHT,
} WindowCorner;
-/// By default, all windows are drawn on a single rectangular grid, represented by
-/// this ScreenGrid instance. In multigrid mode each window will have its own
-/// grid, then this is only used for global screen elements that hasn't been
-/// externalized.
-///
-/// Note: before the screen is initialized and when out of memory these can be
-/// NULL.
-EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
-
-#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
-
// Maximum columns for terminal highlight attributes
#define TERM_ATTRS_MAX 1024
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 60ad8609a4..f76158ff12 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -635,7 +635,7 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot,
// ideally win_update() should keep track of this itself and not scroll
// the invalid space.
if (curgrid->attrs[curgrid->line_offset[r - curgrid->comp_row]
- + left - curgrid->comp_col] >= 0) {
+ + (size_t)left - (size_t)curgrid->comp_col] >= 0) {
compose_line(r, left, right, 0);
}
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9cf1e3eca1..bbae2bfaaf 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -806,7 +806,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
col += parent->w_wincol;
ScreenGrid *grid = &parent->w_grid;
int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
+ grid_adjust(&grid, &row_off, &col_off);
row += row_off;
col += col_off;
}
@@ -877,7 +877,7 @@ void ui_ext_win_position(win_T *wp)
if (win) {
grid = &win->w_grid;
int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
+ grid_adjust(&grid, &row_off, &col_off);
row += row_off;
col += col_off;
if (c.bufpos.lnum >= 0) {