aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGregory Anders <8965202+gpanders@users.noreply.github.com>2023-12-08 18:44:16 -0800
committerGitHub <noreply@github.com>2023-12-09 10:44:16 +0800
commitc651fb30427a3c645b4b1bd8a9b7e767af51e014 (patch)
treeacb7e1d90206e71239cadce2a84f5ca1402845e2 /src
parentf45bf44176461992e7ed7c381aa9750e8955517f (diff)
downloadrneovim-c651fb30427a3c645b4b1bd8a9b7e767af51e014.tar.gz
rneovim-c651fb30427a3c645b4b1bd8a9b7e767af51e014.tar.bz2
rneovim-c651fb30427a3c645b4b1bd8a9b7e767af51e014.zip
refactor(tui): use synchronized updates around actual buf flush (#26478)
Rather than writing the synchronized update begin and end sequences into the TUI's internal buffer (where it is later flushed to the TTY), write these sequences directly to the TTY before and after the TUI's internal buffer is itself flushed to the TTY. This guarantees that a synchronized update is always used when we are actually sending data to the TTY. This means we do not need to keep track of the TUI's "dirty" state (any sequences which affect the TUI state will be written in the TUI's internal buffer, which is now guaranteed to only ever be written when a synchronized update is active).
Diffstat (limited to 'src')
-rw-r--r--src/nvim/tui/tui.c137
1 files changed, 75 insertions, 62 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));
}