From dfed74188d699bdffac18cf23c33b5fcc1984a9f Mon Sep 17 00:00:00 2001 From: fredizzimo Date: Tue, 25 Jul 2023 15:37:07 +0300 Subject: [PATCH] [Backport release-0.9] fix(ui): delay win_viewport until screen update #24182 (#24480) fix(ui): delay win_viewport until screen update #24182 Problem: Sometimes, when nvim sends the `win_viewport` event, for example when scrolling with visible folds on the screen, it reports the `scroll_delta` value one batch into "future". So when the client application is trying to show the new viewport it's not yet updated, resulting in temporary corruption / screen flickering. For more details see #23609, and starting from [this comment]( https://github.com/neovide/neovide/pull/1790#issuecomment-1518697747) in https://github.com/neovide/neovide/pull/1790,, where the issue was first detected. Note that some of the conclusions in those are not fully accurate, but the general observations are. Solution: When there are pending updates to a Window, delay the `win_viewport` UI event until the updates are sent. This ensures that there's no flush between sending the viewport and updating of the lines corresponding to the new viewport. Document the existing viewport behaviour (for cases where there are no extra flushes), give a hint about how applications can deal with the slightly surprising behaviour of the viewport event being sent after the updates. Fixes https://github.com/neovim/neovim/issues/23609 --- runtime/doc/ui.txt | 7 +++++++ src/nvim/window.c | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index a40ce928e8..781d759c82 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -642,6 +642,13 @@ tabs. purpose it only counts "virtual" or "displayed" lines, so folds only count as one line. + All updates, such as `grid_line`, in a batch affects the new viewport, + despite the fact that `win_viewport` is received after the updates. + Applications implementing, for example, smooth scrolling should take + this into account and keep the grid separated from what's displayed on + the screen and copy it to the viewport destination once `win_viewport` + is received. + ["win_extmark", grid, win, ns_id, mark_id, row, col] ~ Updates the position of an extmark which is currently visible in a window. Only emitted if the mark has the `ui_watched` attribute. diff --git a/src/nvim/window.c b/src/nvim/window.c index 0fe5d68c9e..26c533f2a3 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -987,7 +987,9 @@ void ui_ext_win_position(win_T *wp, bool validate) void ui_ext_win_viewport(win_T *wp) { - if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) { + // NOTE: The win_viewport command is delayed until the next flush when there are pending updates. + // This ensures that the updates and the viewport are sent together. + if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid && wp->w_redr_type == 0) { int botline = wp->w_botline; int line_count = wp->w_buffer->b_ml.ml_line_count; if (botline == line_count + 1 && wp->w_empty_rows == 0) {