aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/tui/tui.c137
-rw-r--r--test/functional/terminal/tui_spec.lua3
2 files changed, 76 insertions, 64 deletions
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index b7da3e4b96..eac59eacac 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -42,9 +42,6 @@
# include "nvim/os/tty.h"
#endif
-// Space reserved in two output buffers to make the cursor normal or invisible
-// when flushing. No existing terminal will require 32 bytes to do that.
-#define CNORM_COMMAND_MAX_SIZE 32
#define OUTBUF_SIZE 0xffff
#define TOO_MANY_EVENTS 1000000
@@ -76,7 +73,6 @@ struct TUIData {
size_t bufpos;
TermInput input;
uv_loop_t write_loop;
- bool dirty; ///< whether there has been drawing since the last tui_flush()
unibi_term *ut;
char *term; ///< value of $TERM
union {
@@ -901,8 +897,6 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool
return;
}
- tui_set_dirty(tui);
-
cursor_goto(tui, row, col);
char buf[MAX_SCHAR_SIZE];
@@ -928,8 +922,6 @@ static void clear_region(TUIData *tui, int top, int bot, int left, int right, in
{
UGrid *grid = &tui->grid;
- tui_set_dirty(tui);
-
update_attrs(tui, attr_id);
// Background is set to the default color and the right edge matches the
@@ -1247,8 +1239,6 @@ void tui_grid_scroll(TUIData *tui, Integer g, Integer startrow, Integer endrow,
|| tui->can_set_left_right_margin)));
if (can_scroll) {
- tui_set_dirty(tui);
-
// Change terminal scroll region and move cursor to the top
if (!tui->scroll_region_is_full_screen) {
set_scroll_region(tui, top, bot, left, right);
@@ -1318,48 +1308,6 @@ void tui_default_colors_set(TUIData *tui, Integer rgb_fg, Integer rgb_bg, Intege
invalidate(tui, 0, tui->grid.height, 0, tui->grid.width);
}
-static void tui_set_dirty(TUIData *tui)
- FUNC_ATTR_NONNULL_ALL
-{
- if (!tui->dirty) {
- tui->dirty = true;
- tui_flush_start(tui);
- }
-}
-
-/// Begin flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates,
-/// begin a synchronized update. Otherwise, hide the cursor to avoid cursor jumping.
-static void tui_flush_start(TUIData *tui)
- FUNC_ATTR_NONNULL_ALL
-{
- if (tui->sync_output && tui->unibi_ext.sync != -1) {
- UNIBI_SET_NUM_VAR(tui->params[0], 1);
- unibi_out_ext(tui, tui->unibi_ext.sync);
- } else if (!tui->is_invisible) {
- unibi_out(tui, unibi_cursor_invisible);
- tui->is_invisible = true;
- }
-}
-
-/// Finish flushing the TUI. If 'termsync' is set and the terminal supports synchronized updates,
-/// end a synchronized update. Otherwise, make the cursor visible again.
-static void tui_flush_end(TUIData *tui)
- FUNC_ATTR_NONNULL_ALL
-{
- if (tui->sync_output && tui->unibi_ext.sync != -1) {
- UNIBI_SET_NUM_VAR(tui->params[0], 0);
- unibi_out_ext(tui, tui->unibi_ext.sync);
- }
- bool should_invisible = tui->busy || tui->want_invisible;
- if (tui->is_invisible && !should_invisible) {
- unibi_out(tui, unibi_cursor_normal);
- tui->is_invisible = false;
- } else if (!tui->is_invisible && should_invisible) {
- unibi_out(tui, unibi_cursor_invisible);
- tui->is_invisible = true;
- }
-}
-
void tui_flush(TUIData *tui)
{
UGrid *grid = &tui->grid;
@@ -1376,8 +1324,6 @@ void tui_flush(TUIData *tui)
tui_busy_stop(tui); // avoid hidden cursor
}
- tui_set_dirty(tui);
-
while (kv_size(tui->invalid_regions)) {
Rect r = kv_pop(tui->invalid_regions);
assert(r.bot <= grid->height && r.right <= grid->width);
@@ -1405,10 +1351,7 @@ void tui_flush(TUIData *tui)
cursor_goto(tui, tui->row, tui->col);
- assert(tui->dirty);
- tui_flush_end(tui);
flush_buf(tui);
- tui->dirty = false;
}
/// Dumps termcap info to the messages area, if 'verbose' >= 3.
@@ -2312,23 +2255,93 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
}
}
+/// Write the sequence to begin flushing output to `buf`.
+/// If 'termsync' is set and the terminal supports synchronized output, begin synchronized update.
+/// Otherwise, hide the cursor to avoid cursor jumping.
+///
+/// @param buf the buffer to write the sequence to
+/// @param len the length of `buf`
+static size_t flush_buf_start(TUIData *tui, char *buf, size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *str = NULL;
+
+ if (tui->sync_output && tui->unibi_ext.sync != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 1);
+ str = unibi_get_ext_str(tui->ut, (size_t)tui->unibi_ext.sync);
+ } else if (!tui->is_invisible) {
+ str = unibi_get_str(tui->ut, unibi_cursor_invisible);
+ tui->is_invisible = true;
+ }
+
+ if (str == NULL) {
+ return 0;
+ }
+
+ return unibi_run(str, tui->params, buf, len);
+}
+
+/// Write the sequence to end flushing output to `buf`.
+/// If 'termsync' is set and the terminal supports synchronized output, end synchronized update.
+/// Otherwise, make the cursor visible again.
+///
+/// @param buf the buffer to write the sequence to
+/// @param len the length of `buf`
+static size_t flush_buf_end(TUIData *tui, char *buf, size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t offset = 0;
+ if (tui->sync_output && tui->unibi_ext.sync != -1) {
+ UNIBI_SET_NUM_VAR(tui->params[0], 0);
+ const char *str = unibi_get_ext_str(tui->ut, (size_t)tui->unibi_ext.sync);
+ offset = unibi_run(str, tui->params, buf, len);
+ }
+
+ const char *str = NULL;
+ bool should_invisible = tui->busy || tui->want_invisible;
+ if (tui->is_invisible && !should_invisible) {
+ str = unibi_get_str(tui->ut, unibi_cursor_normal);
+ tui->is_invisible = false;
+ } else if (!tui->is_invisible && should_invisible) {
+ str = unibi_get_str(tui->ut, unibi_cursor_invisible);
+ tui->is_invisible = true;
+ }
+
+ if (str != NULL) {
+ assert(len >= offset);
+ offset += unibi_run(str, tui->params, buf + offset, len - offset);
+ }
+
+ return offset;
+}
+
static void flush_buf(TUIData *tui)
{
uv_write_t req;
- uv_buf_t buf;
+ uv_buf_t bufs[3];
+ char pre[32];
+ char post[32];
if (tui->bufpos <= 0) {
return;
}
- buf.base = tui->buf;
- buf.len = UV_BUF_LEN(tui->bufpos);
+ bufs[0].base = pre;
+ bufs[0].len = UV_BUF_LEN(flush_buf_start(tui, pre, sizeof(pre)));
+
+ bufs[1].base = tui->buf;
+ bufs[1].len = UV_BUF_LEN(tui->bufpos);
+
+ bufs[2].base = post;
+ bufs[2].len = UV_BUF_LEN(flush_buf_end(tui, post, sizeof(post)));
if (tui->screenshot) {
- fwrite(buf.base, buf.len, 1, tui->screenshot);
+ for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) {
+ fwrite(bufs[i].base, bufs[i].len, 1, tui->screenshot);
+ }
} else {
int ret
- = uv_write(&req, (uv_stream_t *)&tui->output_handle, &buf, 1, NULL);
+ = uv_write(&req, (uv_stream_t *)&tui->output_handle, bufs, ARRAY_SIZE(bufs), NULL);
if (ret) {
ELOG("uv_write failed: %s", uv_strerror(ret));
}
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 4d2f20d6d5..9978bcca9d 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -1671,10 +1671,9 @@ describe('TUI', function()
-- Use full screen message so that redrawing afterwards is more deterministic.
child_session:notify('nvim_command', 'intro')
screen:expect({any = 'Nvim'})
- -- Hiding the cursor needs 6 bytes.
-- Going to top-left corner needs 3 bytes.
-- Setting underline attribute needs 9 bytes.
- -- The whole line needs 6 + 3 + 9 + 65513 + 3 = 65534 bytes.
+ -- The whole line needs 3 + 9 + 65513 + 3 = 65528 bytes.
-- The cursor_address that comes after will overflow the 65535-byte buffer.
local line = ('a'):rep(65513) .. '℃'
child_session:notify('nvim_exec_lua', [[