When moving the viewport in the scrollback (i.e. “scrolling”), we need
to ensure the viewport is not moved past the beginning, or end, of the
scrollback.
This was previously accomplish by first limiting the number of lines
to scroll to the number of visible rows (i.e the viewport _size_), and
by adjusting the viewport after moving it, to ensure it doesn’t point
into an uninitialized scrollback area etc.
I.e. the implementation was _reactive_.
This patch rewrites the logic to be _proactive_; we now calculate
_where_ the beginning (or end) of the scrollback is, and then how many
lines there is from there, to the viewport. This is our _maximum_
number of lines to scroll.
When done correctly (which I hope this patch does), this allows us to
remove _all_ checks after moving the viewport - we already _know_ it’s
correct, and valid.
As a bonus, we can remove the old limit, where scrolling was only
allowed to be at most a single page.
* Allow scrolling on the normal (non-alt) screen, when application is
grabbing the mouse (when user presses Shift).
* Use term_mouse_grabbed() instead of explicitly checking for
MOUSE_NONE tracking.
* Remove mouse tracking check from cmd_scrollback_{up,down}. Caller is
expected to have done the check.
* Don’t scroll down on mouse wheel tilt events.
This allows us to update the jump label positions when the viewport
changes.
This in turn allows us to stay in URL mode while the user is using the
mouse to scroll in the scrollback history.
Scrolling with the keyboard is currently not possible, since input
handling in URL mode does not recognize “regular” key bindings. We
_could_ add scrollback up/down bindings to URL mode too, but lets not,
for the time being.
(Note: an alternative to this patch is to disallow mouse scrolling
too. Then we could have kept the URL start/end as viewport local
coordinates).
When making sure we don't scroll past the scrollback history, and the
current offset was exactly at the wrap around, the new view port was
set to offset+1, which in this particular case meant outside the valid
row numbers.
When we scroll up, we need to ensure that we don't scroll too far,
"past" the scrollback limit. I.e. we need to ensure we don't wrap
around.
The code did this. But, in certain scenarios, the resulting view
points into uninitialized scrollback history.
This happens when we haven't yet filled the entire scrollback, and
scroll up enough lines to wrap around the scrollback. The old code
would adjust the view for the wrap around, but doing so pointed the
view at the not-yet utilized scrollback.
When doing "small" scrolls (typically done via mouse wheel or
similar), we render the scrolling by emitting a "scroll damage".
A recent commit changed how scroll damage is rendered; only when the
view is at the bottom ("following" the screen output) do we render the
damage.
To fix this, add a new type of scroll damage,
SCROLL_DAMAGE_IN_VIEW and SCROLL_DAMAGE_REVERSE_IN_VIEW.
These signal to the renderer that it should always render the damage.
With this assumption, we can replace 'a % b' with 'a & (b - 1)'. In
terms of instructions, this means a fast 'and' instead of a slow
'div'.
Further optimize scrolling by:
* not double-initializing empty rows. Previously, grid_row_alloc()
called calloc(), which was then followed by a memset() when
scrolling. This is of course unnecessary.
* Don't loop the entire set of visible rows (this was done to ensure
all visible rows had been allocated, and to prefetch the cell
contents).
This isn't necessary; only newly pulled in rows can be NULL. For
now, don't prefetch at all.
When scrolling through the scrollback lines, use scroll damage instead
of re-rendering the entire screen whenever it makes sense. I.e. when
the number of lines isn't a whole page or more.
The row array may now contain NULL pointers. This means the
corresponding row hasn't yet been allocated and initialized.
On a resize, we explicitly allocate the visible rows.
Uninitialized rows are then allocated the first time they are
referenced.
Can scroll up and down, and stops when the beginning/end of history is
reached.
However, it probably breaks when the entire scrollback buffer has been
filled - we need to detect when the view has wrapped around to the
current terminal offset.
The detection of when we've reached the bottom of the history is also
flawed, and only works when we overshoot the bottom with at least a
page.
Resizing the windows while in a view most likely doesn't work.
The view will not detect a wrapped around scrollback buffer. I.e. if
the user has scrolled back, and is stationary at a view, but there is
still output being produced. Then eventually the scrollback buffer
will wrap around. In this case, the correct thing to do is make the
view start following the beginning of the history. Right now it
doesn't, meaning once the scrollback buffer wraps around, you'll start
seeing command output...