This patch adds a `confined` flag to each cell to track if the last
rendered glyph bled into it's right neighbor. To keep things simple,
bleeding into any other neighbor cell than the immediate right one is
not allowed. This should cover most use cases.
Before rendering a row we now do a prepass and mark all cells unclean
that are affected by a bleeding neighbor. If there are consecutive
bleeding cells, the whole group must be re-rendered even if only a
single cell has changed.
The patch also deprecates both old overflowing glyph options
*allow-overflowing-double-width-glyphs* and *pua-double-width* in favor
of a single new one named *overflowing-glyphs*.
It may happen that we end up with multiple non-busy, same-sized
buffers for the same cookie (context), and thus eligible for re-use.
Before this patch, we would keep all those buffers around. This is
completely unnecessary. Under normal circumstances, we’ll either be
re-using a single buffer, or swap between two. In the second case, the
“other” buffer is always busy, and thus not eligible for re-use.
So, if we _do_ detect multiple, re-usable buffers, pick the one with
the lowest “age” (increasing the chance of applying damage tracking,
instead of re-drawing everything), and mark the other one for purging.
If we have lots of URLs, we use up a *lot* of SHM buffers (and thus
pools). Each and every one is a single mmap(), of at least 4K.
Since all URL labels are created and destroyed at the same time, it
makes sense to use a single pool for all buffers.
To do this, we must now do two passes; first one to generate the
actual string (label content), and to calculate the label sizes.
Then we use this information to allocate all SHM buffers at once, from
the same pool.
Finally, we iterate the URLs again, this time to actually render them.
shm_get_many() always returns new buffers (i.e. never old, cached
ones). The newly allocated buffers are also marked for immediate
purging, meaning they’ll be destroyed on the next call to either
shm_get_buffer(), or shm_get_many().
Furthermore, we add a new attribute, ‘locked’, to the buffer
struct. When auto purging buffers, look at this instead of comparing
cookies.
Buffer consumers are expected to set ‘locked’ while they hold a
reference to it, and don’t want it destroyed behind their back.
This fixes an issue where we ended up "double closing" buffer FDs.
In many cases (especially on compositors with SSDs) this was pretty
rare. And even when it did happen, the FD was normally unused, and
thus nothing bad happened.
However, by quickly resizing the window while using CSDs, it was
fairly easy to trigger this. We sometimes ended up closing the
TIOCSWINCH timer FD while thinking it was a buffer FD, but most of the
times we just ended up closing _another_ buffer’s pool FD, leading to
an immediate disconnect by the compositor.
There’s a chance the resize timeout FD was closed, and *reused*, after
epoll() told us the FD is readable, but before our callback runs.
Thus, closing the FD provided as an argument is dangerous, as it may
refer to something completely different.
There has been some confusion whether enabling DECSDM (private mode
80) enables or disables sixel scrolling.
Foot currently enables scrolling when DECSDM is set, and this patch
changes this, such that setting DECSDM now *disables* scrolling.
The confusion is apparently due to a documentation error in the VT340
manual, as described in
https://github.com/dankamongmen/notcurses/issues/1782#issuecomment-863603641.
And that makes sense, in a way: the SDM in DECSDM stands for Sixel
Display Mode. I.e. it stands to reason that enabling that disables
scrolling.
Anyway, this lead to https://github.com/hackerb9/lsix/issues/41, where
it was eventually proven (by testing on a real VT340), that foot, and
a large number of other terminals (including XTerm) has it wrong:
https://github.com/hackerb9/lsix/issues/41#issuecomment-873269599.
When tagging URL cells (in preparation for rendering URL mode), we
loop the URL’s entire range, setting the `url` attribute of all cells,
and dirtying the rows.
It is possible to create URLs that are invalid, and wrap around the
scrollback, even though the scrollback hasn’t yet been filled. For
example, by starting an OSC-8 URL, moving the cursor, and then closing
the OSC-8 URL.
These URLs are invalid, but are still rendered just fine. “Fine” being
relative - they will typically fill the entire screen. But at least
that’s a very clear indication for the user that’s something is wrong.
The problem is when we hit un-allocated scrollback rows. We didn’t
check for NULL rows, and crashed.
This has now been fixed.
The only reason to keep the pool FD open is if we’re going to SHM
scroll the buffer; we need the FD for fallocate(FALLOC_FL_PUNCH_HOLE).
In all other cases, there’s absolutely no need to keep the FD
open. Thus, close it as soon as we’ve instantiated the buffer. This
frees up FDs, and help keep foot from FD ulimit.
Removing overlaping and duplicated URLs is done by running two nested
loops, that both iterate the same URL list.
When a duplicate is found, one of the URLs is destroyed and removed
from the list.
Deleting and removing an item *is* safe, but only as long as _no
other_ iterator has references to it.
In this case, if we remove an item from e.g. the inner iterator, we’ll
crash if the outer iterator’s *next* item is the item being removed.
Closes#627
All SHM pixmap cookies depend on the terminal instance’s memory
address. Thus, after a terminal instance has been destroyed, shm
pixmaps that belonged to it will never be purged automatically.
CSDs aren’t typically toggled on and off. Thus, when disabled,
immediately purge their corresponding pixmap buffers, to free up some
memory, and release file descriptors.
Unlike other surface types, the SHM cookie depends on the address of
each URL instance. This means if we enable, disable, and then enable
URL mode again (thus showing exactly the same URLs as the first time),
the URLs will have new addresses, and thus the old SHM pixmaps will
not get purged automatically.
So, manually purge them when destroying the URLs.
That is, instead of requiring a ‘\n’ to be printed, non-empty lines
are now treated as having a hard linebreak by default.
The linebreak is cleared on an explicit wrap.
A narrow, but offset:ed glyph should still be considered double
width.
This patch also fixes a crash, when the maybe-double width glyph is in
the last column. This is a regression.