From 874ce8239cd7ebcf8e12effdc18e0ccd7c477d73 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 30 Dec 2019 23:23:42 +0000 Subject: [PATCH] Fix handling of scroll position when swapping screens. If the user is scrolled back in the scrollback when a screen-swap takes place, and if we're not configured to reset the scrollback completely on the grounds that the swap is display activity, then we should do the same thing we do for other kinds of display activity: strive to keep the scroll position pointing at the same text. In this case, that means adjusting term->disptop by the number of virtual lines added to the scrollback to allow the main screen to be viewed while the alt screen is active. This improves the quality of behaviour in that corner case, but more importantly, it should also fix a case of the dreaded line==NULL assertion failure, which someone just reported against 0.73 when exiting tmux (hence, switching away from the alt screen) while scrolled back in a purely virtual scrollback buffer: the virtual scrollback lines vanished, but disptop was still set to a negative value, which made it out of range. (cherry picked from commit 22453b46daf2b03f5fe8b2bfac35b818f6c789fe) --- terminal.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/terminal.c b/terminal.c index 8d682ccb..ad54463b 100644 --- a/terminal.c +++ b/terminal.c @@ -2046,6 +2046,21 @@ static void swap_screen(Terminal *term, int which, reset = false; /* do no weird resetting if which==0 */ if (which != term->alt_which) { + if (term->erase_to_scrollback && term->alt_screen && + term->alt_which && term->disptop < 0) { + /* + * We're swapping away from the alternate screen, so some + * lines are about to vanish from the virtual scrollback. + * Adjust disptop by that much, so that (if we're not + * resetting the scrollback anyway on a display event) the + * current scroll position still ends up pointing at the + * same text. + */ + term->disptop += term->alt_sblines; + if (term->disptop > 0) + term->disptop = 0; + } + term->alt_which = which; ttr = term->alt_screen; @@ -2120,6 +2135,26 @@ static void swap_screen(Terminal *term, int which, if (!reset) term->save_sco_acs = term->alt_save_sco_acs; term->alt_save_sco_acs = t; + + if (term->erase_to_scrollback && term->alt_screen && + term->alt_which && term->disptop < 0) { + /* + * Inverse of the adjustment at the top of this function. + * This time, we're swapping _to_ the alternate screen, so + * some lines are about to _appear_ in the virtual + * scrollback, and we adjust disptop in the other + * direction. + * + * Both these adjustments depend on the value stored in + * term->alt_sblines while the alt screen is selected, + * which is why we had to do one _before_ switching away + * from it and the other _after_ switching to it. + */ + term->disptop -= term->alt_sblines; + int limit = -sblines(term); + if (term->disptop < limit) + term->disptop = limit; + } } if (reset && term->screen) {