Foot currently does reverse-wrapping (‘auto_left_margin’, or ’bw’) on
everything that calls ‘term_cursor_left()’. This is wrong; it should
only be done for cub1. From man terminfo:
auto_left_margin | bw | bw | cub1 wraps from column 0 to last
column
This patch moves the reverse-wrapping logic from term_cursor_left() to
the handling of BS (backspace).
Closes#441
This is done by:
* Not limiting the number of times we try to read from the PTY when
we’ve have POLLHUP
* Not requiring the entire the previous read to have filled our
buffer.
* Not erroring out on EIO.
This ensures different seat’s don’t step on each others IME pre-edit
state.
It also removes most dependencies on having a valid term pointer for
many IME operations.
We’re still not all the way, since we support disabling IME with a
private mode, which is per terminal, not seat.
Thus, we still require the seat to have keyboard focus on one of our
windows.
Closes#324. But note that *rendering* of multiple seat’s IME pre-edit
strings is still broken.
The line break flag is used by the text reflow and text
extraction (i.e. copy-paste) logic, to determine whether or not to
insert a newline between two lines.
There’s some amount of heuristics involved in this. For example, the
client application could emit a newline, move the cursor back up, and
print text. What does that mean for us?
Foot’s behavior up until now has been this:
The line break flag is set on the row, when the application emits an
explicit linefeed. The flag is cleared when the line is erased. But
otherwise not.
This meant that emitting a linefeed and then moving the cursor back up
and printing text, did not clear the line break flag. This in turn
meant that text copied always had newlines inserted, even though that
was not the client applications intention.
By clearing the line break flag whenever _anything_ is printed to a
row, the new behavior is, in practice, that the line break flag is
only set on a row if a linefeed was that *last* thing printed to that
row.
Closes#410
When the user has set a custom line-height, we now adjust it when
increasing/decreasing (“zooming”) the font size at run-time.
Previously, the line-height was fixed at the size specified in
foot.ini.
term_print() is called whenever the client application “prints”
something to the grid. It is called for both ASCII and UTF-8
characters, and needs to handle sixels, insert mode and ASCII
vs. graphical charsets.
Since it’s on the hot path, this becomes unnecessarily slow.
This patch adds a “fast” version of term_print(), tailored for the
common case: ASCII characters in non-insert mode, without any sixels
and non-graphical charsets.
A new function, term_update_ascii_printer(), has been added, and must
be called whenever:
* The currently selected charset *index* changes
* The currently selected charset changes (from ASCII to graphical, or
vice verse)
* Sixels are added to the grid
* Sixels are removed from the grid
* Insert mode is enabled/disabled
Closing it as soon as we detect that the client has died, means we may
not have drained it completely.
The PTY is either closed _by_ the client application, or by us when we
shutdown the terminal. Thus, leaving it open (until we call
term_shutdown()) is fine.
When enabled (the default), sixels behave much like normal output; the
start where the cursor is, and the cursor moves with the
sixel. I.e. after emitting a sixel the cursor is left after the image;
either to the right, if private mode 8452 is enabled, or otherwise on
the next line. Terminal content is scrolled up if the sixel is larger
than the screen.
When disabled, sixels *always* start at (0,0), the cursor never moves,
and the terminal content never scrolls.
In other words, the ‘disabled’ mode is a much simpler mode.
All we need to do to support both modes is re-write the sixel-emitting
loop to:
* break early if we’re “out of rows”, i.e. we’ve reached the bottom of
the screen.
* not linefeed, or move the cursor when scrolling is disabled
This patch also fixes a bug in the (new) implementation of private
mode 8452.
When emitting a sixel, we may break it up into smaller pieces, to
ensure a single sixel (as tracked internally) does not cross the
scrollback wrap-around.
The code that checked if we should do a linefeed or not, would skip
the linefeed on the last row of *each* such sixel piece. The correct
thing to do is to skip it only on the last row of the *last* piece.
I chose not to fix this bug in a separate patch since doing so would
have meant re-writing it again when implementing private mode 80.
Previously, we automatically exited URL mode whenever we received data
on the PTY. This was done since we don’t know _what_ has changed on
the screen, and we don’t want to display misleading jump labels.
However, this becomes a problem in curses-like applications that
periodically updates part of the screen. For example, a statusbar with
a clock.
This patch changes this behavior; instead of cancelling URL mode when
receiving PTY data, we snapshot the grid when entering URL mode.
When *rendering*, we use the snapshot:ed grid, while PTY updates
modify the “real” grid.
Snapshot:ing the grid means taking a full/deep copy of the current
grid, including sixel images etc.
Finally, it isn’t necessary to “damage” the entire view
when *entering* URL mode, since we’re at that point the renderer is in
sync with the grid. But we *do* need to damage the entire view when
exiting URL mode, since the grid changes on the “real” grid hasn’t
been tracked by the renderer.
When enabled (the default), sixels use private color registers. That
is, the color palette from the last sixel is *not* re-used.
When disabled, sixels share (i.e. re-use) the same color palette.
Closes#362
This patch adds a new configuration option,
‘osc8-underline=url-mode|always’.
When set to ‘url-mode’, OSC-8 URLs are only
highlighted (i.e. underlined) in url-mode, just like auto-detected
URLs.
When set to ‘always’, they are always underlined, regardless of mode,
and regardless of their other attributes.
This is implemented by tagging collected URLs with a boolean,
instructing urls_render() and urls_reset() whether they should update
the cells’ ‘url’ attribute or not.
The OSC-8 collecter sets this based on the value of ‘osc8-underline’.
Finally, when closing an OSC-8 URL, the cells are immediately tagged
with the ‘url’ attribute if ‘osc8-underline’ is set to ‘always’.
If the client application emitted e.g:
\E]8;;http://foo\E\\\E]8;;\E\\
i.e. an anchor without content, then we ended up with an ‘end’
coordinate that lied *before* the ‘start’ coordinate, since we
subtract one (column) from the end point to make it inclusive.
These functions update the OSC-8 URI state in the terminal.
term_osc8_open() tracks the beginning of an URL, by storing the start
coordinate (i.e. the current cursor location), along with the URL
itself.
Note that term_osc8_open() may not be called with an empty URL. This
is important to notice, since the way OSC-8 works, applications close
an URL by “opening” a new, empty one:
\E]8;;https://foo.bar\e\\this is an OSC-8 URL\E]8;;\e\\
It is up to the caller to check for this, and call term_osc8_close()
instead of term_osc8_open() when the URL is empty.
However, it is *also* valid to switch directly from one URL to
another:
\E]8;;http://123\e\\First URL\E]8;;http//456\e\\Second URL\E]8;;\e\\
This use-case *is* handled by term_osc8_open().
term_osc8_close() uses the information from term_osc8_open() to add
per-row URL data (using the new ‘extra’ row data).
This patch adds an ‘extra’ member to the row struct. It is a pointer
to a struct containing extra data to be associated with this row.
Initially, this struct contains a list of URL ranges. These
define (OSC-8) URLs on this row.
The ‘extra’ data is allocated on-demand. I.e. the pointer is NULL by
default; it is *not* allocated by grid_row_alloc().
In addition to letting the FDM do the low-level signal watching, this
patch also fixes a bug; multiple SIGCHLDs, be it delivered either through a
signal, or via a signalfd, can be coalesced, like all signals.
This means we need to loop on waitpid() with WNOHANG until there are
no more processes to reap.
This in turn requires a small change to the way reaper callbacks are
implemented.
Previously, the callback was allowed to do the wait(). This was
signalled back to the reaper through the callback’s return value.
Now, since we’ve already wait():ed, the process’ exit status is passed
as an argument to the reaper callback.
The callback for the client application has been updated accordingly;
it sets a flag in the terminal struct, telling term_destroy() that the
process has already been wait():ed on, and also stores the exit
status.
In many places we have the following pattern:
tll_foreach(list, it)
free(it->item.thing);
tll_free(list);
Since all tll functions are macros, and thus inlined, and since
tll_free in itself expands to a tll_foreach(), the above pattern
expands to more native code than necessary.
This is somewhat smaller:
tll_foreach(list, it) {
free(it->item.thing);
tll_remove(list, it);
}
This is implemented by allocating one of the (few!) remaining bits in
the cells’ attribute struct to indicate the cell should be “URL
highlighted”.
render_cell() looks at this bit and draws an underline using the color
from colors.urls (defaults to regular3 - i.e. yellow).
A new function, url_tag_cells(), iterates the currently detected URLs
and sets the new ‘url’ attribute flag on the affected cells.
Note: this is done in a separate function to keep urls_collect() free
from as many dependencies as possible.
urls_reset() is updated to *clear* the ‘url’ flag (and thus implicitly
also results in a grid refresh, _if_ there were any URLs).
We now exit URL mode on *any* client application input. This needs to
be so since we can’t know if the URLs we previously detected are still
valid.
These two buttons were encoded using the *exact* same numbers as
button 4 and 5 (scroll wheel up/down), making it impossible to
distinguish them.
The relevant section from XTerm’s control sequences documentation is:
Some wheel mice can send additional button events, e.g., by tilting the
scroll wheel left and right.
Additional buttons are encoded like the wheel mice,
o by adding 64 (for buttons 6 and 7), or
o by adding 128 (for buttons 8 through 11).
terminal.c:3:10: fatal error: 'malloc.h' file not found
#include <malloc.h>
^~~~~~~~~~
terminal.c:1512:9: error: implicit declaration of function 'sigaction' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
sigaction(SIGALRM, &(const struct sigaction){.sa_handler = &sig_alarm}, NULL);
^
terminal.c:1532:21: error: implicit declaration of function 'kill' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
kill(term->slave, kill_signal);
^