diff options
-rw-r--r-- | src/nvim/tui/tui.c | 137 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 3 |
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', [[ |