Without this, `wzsh` will keep the terminal in raw mode between
line editor invocations, resulting in staggered/stairway output
for any spawned commands.
With the revised native windows console renderer using the various
console APIs more deeply, I've seen a couple of cases where those
API calls fail inside eg: wezterm running via the new pty machinery.
Using the virtual terminal APIs and the terminfo renderer is the
right thing to do in that case.
This commit probes for virtual terminal support and uses the builtin
xterm terminfo, unless the environment has
`TERMWIZ_BYPASS_VIRTUAL_TERMINAL=1` set. This allows forcing the
use of the windows console layer.
Some windows APIs have inclusive dimensions and some exclusive;
we were off by one for the height of the display which led to some
weirdness with eg: `sp` and the line editor.
When it comes to scrolling: if the scroll request is for the entire
viewport then we simply adjust the viewport; this is desirable because
it allows data to scroll back into the history in the native console.
This fixes the math around cursor positioning for the edge case where
the width of text and the cursor position are close to the width of
the terminal.
This reduces flickering updates in the native windows console;
it works by taking a copy of the screen buffer, applying the
Change's to that buffer and then copying back to the console.
This is unfortunately a bit of a muddy commit and I'm too lazy to split
it up.
* Removed `Position::NoChange`; use `Position::Relative(0)` instead
* Added missing cursor positioning cases in the terminfo renderer
* Taught line editor about the cursor position when the line spans
multiple physical lines
* Taught the Windows input layer to process escape sequences for eg:
the arrow keys when running with virtual terminal enabled.
* Removed the hack that under-reported the terminal width; the hack
was present to make some aspects of rendering with the native windows
console logic easier, but it was getting in the way of the line
editor. This may well break something, but it fixed up the line
editor :-/
cc: @markbt
This commit changes the behavior on Windows:
* If $TERM is set and the `terminfo` crate is able to
successfully initialize and locate a terminfo database (this also
requires that $TERMINFO be set in the environment), then we'll
use the `TerminfoRenderer` instead of the `WindowsConsoleRenderer`
* If $TERM is set to `xterm-256color` and no terminfo database was
found, use our modern compiled-in copy (look in the `termwiz/data/`
directory for the source and compiled version of this) and use
the `TerminfoRenderer`.
* Otherwise use the `WindowsConsoleRenderer`.
In practice, this allows termwiz apps to opt in to features such as
true color support on Windows 10 build 1903 an later by setting their
`TERM=xterm-256color`. This happens to be the default behavior when
`ssh`ing in to a windows host via `wezterm`.
You can see the truecolor mode get applied by running this example:
```
cargo run --example widgets_basic --features widgets
```
with TERM set as above the background region that is painted by the app
will be a blueish/purplish color, but with it unset or set to something
invalid, it will fall back to black.
I'd like to eventually make termwiz assume the equivalent configuration
to `TERM=xterm-256color` by default on Windows 10 build 1903 and later,
but it's worth getting some feedback on how this works for clients such
as `streampager`.
cc: @quark-zju and @markbt
Add an update indicator to the top right of client tabs; this is
overlaid on top of the surface when the last update from the server was
more than ~3s ago and if we expected it sooner than that.
While making this work, I noticed that the exponential poll backoff
had gotten broken in an earlier refactor; instead of a series of polls
backing off slowly, we were aggressively running the backoff up to the
max 30 second interval over the span of a few ms. This commit fixes
up the backoff computation to only happen when we are ready to send
a poll.
refs: https://github.com/wez/wezterm/issues/127
derive_builder has some extra dependencies that take a while to compile.
The builder feature can be expressed via a 30-line macro. So let's do
that to make termwiz compile faster.
The palette crate has a codegen step that translates svg_colors.txt to named.rs.
That makes it hard to build using buck.
Remove the palette dependency so termwiz is easier to build using buck.
I made sure the following code:
fn main() {
use termwiz::color::RgbColor;
let r = RgbColor::from_rgb_str("#02abcd").unwrap();
let r1 = r.to_tuple_rgba();
let r2 = r.to_linear_tuple_rgba();
println!("r1 = {:?}", r1);
println!("r2 = {:?}", r2);
}
prints
r1 = (0.007843138, 0.67058825, 0.8039216, 1.0)
r2 = (0.000607054, 0.4072403, 0.6104956, 1.0)
before and after the change.
Embed rgb.txt and parse it on the fly to produce the list of colors.
This list is a superset of palette's SVG color list.
refs: https://github.com/wez/wezterm/pull/144
This makes the input behavior consistent with posix: if SHIFT is held
and a letter key is pressed, make sure that we treat that as the ascii
uppercase version of that key and that the SHIFT modifier is cleared.
Adds logic to resize handling that will consider the original logical
line length when the width of the terminal is changed.
The intent is that this will cause the text to be re-flowed as if it had
been printed into the terminal at the new width. Lines that were
wrapped due to hittin the margin will be un-wrapped and made into a
single logical line, and then split into chunks of the new width.
This can cause new lines to be generated in the scrollback when
making the terminal narrower. To avoid losing the top of the buffer
in that case, the rewrapping logic will prune blank lines off the
bottom.
This is a pretty simplistic brute force algorithm: each of the lines
will be visited and split, and for large scrollback buffers this could
be relatively costly with a busy live resize. We don't have much choice
in the current implementation.
refs: https://github.com/wez/wezterm/issues/14
`cargo run --example widgets_basic --features widgets` changes the
cursor style but wasn't changing it back when exiting.
In addition, setting the cursor to Default was only restoring visibility
and not restoring the style.
I was running `hg log --config pager.pager=sp` and pressing space without
releasing it. After about 10k lines sp appears to deadlock. It seems sp uses a
single thread for both reading terminal events and sending wake events and it
sends too many wake events without reading the events.
Failing to write to the wake pipe due to EWOULDBLOCK does not seem to be a
problem - there are enough events in the pipe to wake up the other side.
Therefore let's just make wake_pipe_write nonblocking and treat EWOULDBLOCK as
a success.
Context: The stuck thread looks like:
#0 0x00007f32671ee237 in write () from /usr/lib/libc.so.6
#1 0x000055c466022823 in std::sys::unix::fd::FileDesc::write () at src/libstd/sys/unix/fd.rs:96
#2 std::sys::unix::net::Socket::write () at src/libstd/sys/unix/net.rs:276
#3 <&std::sys::unix::ext::net::UnixStream as std::io::Write>::write ()
at src/libstd/sys/unix/ext/net.rs:597
#4 <std::sys::unix::ext::net::UnixStream as std::io::Write>::write ()
at src/libstd/sys/unix/ext/net.rs:582
#5 0x000055c465d010a6 in termwiz::terminal::unix::UnixTerminalWaker::wake (self=0x7ffe6bd32de0)
at /home/quark/.cargo/git/checkouts/wezterm-6425bab852909cc8/ef1b836/termwiz/src/terminal/unix.rs:278
#6 0x000055c465a6c64b in streampager::event::EventSender::send_unique (self=0x7ffe6bd32dd0, event=...,
unique=0x7ffe6bd32de8) at src/event.rs:66
#7 0x000055c465a7e65a in streampager::display::start (term=..., term_caps=..., events=..., files=...,
error_files=..., progress=..., mode=streampager::config::FullScreenMode::Auto) at src/display.rs:295
#8 0x000055c465a453a7 in streampager::Pager::run (self=...) at src/lib.rs:171
#9 0x000055c465a0aced in sp::open_files (args=...) at src/bin/sp/main.rs:170
#10 0x000055c465a08e4f in sp::main () at src/bin/sp/main.rs:25
This commit adds some plumbing for describing the cursor shape
(block, line, blinking etc) and visibility, and feeds that through
the mux and render layers.
The renderer now knows to omit the cursor when it is not visible.
This isn't complete but begins the process of extracting
the embedding application configuration into a trait provided
by the application rather than passing the values in at
construction.
This allows the application to change configuration at
runtime.
The first option to handle this is the scrollback size.
The defaults are pretty neutral. You can get a little more fancy
with something like this:
```
[colors.tab_bar]
background = "#0b0022"
[colors.tab_bar.active_tab]
bg_color = "#2b2042"
fg_color = "#c0c0c0"
[colors.tab_bar.inactive_tab]
bg_color = "#1b1032"
fg_color = "#808080"
[colors.tab_bar.inactive_tab_hover]
bg_color = "#3b3052"
fg_color = "#909090"
italic = true
```
This is a bit of a large commit because it needed some plumbing:
* Change mux creation to allow deferring associating any domains,
and to change the default domain later in the lifetime of the
program
* De-bounce the empty mux detection to allow for transient windows
during early startup
* Implement a bridge between the termwiz client Surface and the
frontend gui renderer so that we can render from termwiz to
the gui.
* Adjust the line editor logic so that the highlight_line method
can change the length of the output. This enables replacing
the input text with placeholders so that we can obscure password
input
I noticed while scrolling `emoji-test.txt` that some of the combined
emoji sequences rendered very poorly. This was due to the unicode
width being reported as up to 4 in some cases.
Digging into it, I discovered that the unicode width crate uses a
standard calculation that doesn't take emoji combination sequences
into account (see https://github.com/unicode-rs/unicode-width/issues/4).
This commit takes a dep on the xi-unicode crate as a lightweight way
to gain access to emoji tables and test whether a given grapheme is
part of a combining sequence of emoji.
I've noticed this off and on for a while, and thought it was something
fishy with my shell dotfiles.
Tracing through I found that the final byte in the "Face with head
bandage" emoji 🤕 U+1F915 was being interpreted as the MW control
code and causing the vt parser to jump out of the OSC state.
The solution for this is to hook up proper UTF-8 processing in the
same way that it is applied in the ground state.
Since we don't have enough bits to introduce new state values (we're
pretty tightly packed in the 16 bits available), I've introduced a
memory of the state to which the utf8 parser needs to return once
a complete sequence is detected.
I thought that I'd broken something with the DEL processing in vim with
the new frontend but it turned out that the other frontend was emitting
BS always and that I'd actuall unbroken passing DEL through and that
other layers were translating DEL into an application cursor mode output
for DEL that emits a totally different sequence.
This diff preserves DEL and disables that other sequence.
Will follow up with some explicit configuration to control this
behavior, but in the short term, the default behavior should be much
closer to what people actually want and expect!
refs: https://github.com/wez/wezterm/issues/52
This is still a bit rough because the terminal parser doesn't
understand the pixel sizes, so it relies on the hard coded
cell dimensions being accurate.
This enables using large OSC buffers in a form that we can publish
to crates.io without blocking on an external crate. Large OSC
buffers are important both for some tunnelling use cases and for
eg: iTerm2 image protocol handling.
Add a convenience function to the escape parser that, like `parse_first`,
matches only the first escape sequence, but instead collects all matching
actions.
It's taking a while for https://github.com/jwilm/vte/pull/20 to get
merged, so point to my branch directly while I build out some
tunneled mux protocol escape sequences.
I'll need to fork vte on crates.io if vte doesn't merge the PR
before the next termwiz crate bump.
The `flush_pending_attr` method does lots of unnecessary comparisons.
If the attributes have changed, then it works by resetting the
attributes and then setting new values. There's no need to emit the
codes for exiting modes.
It also doesn't support double underscore or rapid blink in the cases
where the terminfo capabilities are used, as these capabilities can't
express these attributes. Fall back to CSI sequences when these
attributes are requested.
Changing the terminal attributes (bold, underline, etc.) involves
emitting the `exit_attribute_mode` or SGR reset sequence. This also
resets the colors back to their defaults. If this happens when the
foreground or background colors haven't changed, set the colors again.
These codes are used to change the color palette, but if the `?`
string is used in place of a color spec, then we must respond with
the current color value string for that palette entry, so lets
implement that!
We need to set the terminal to blocking mode when we want poll_input
to block forever, otherwise we immediately receive WouldBlock error
response and quit the program.
This one has been bugging me for a while; we now know when we've
wrapped a line and can join it without a line break when double-clicking
to select a word.
This commit introduces a wrapped attribute to help record this
information, which could potentially help with when it comes
to looking at nicer resize behavior in refs: https://github.com/wez/wezterm/issues/14
When repainting the screen, we must be sure to set the cursor
to the right shape.
While the repaint is happening, hide the cursor to prevent
the cursor jumping around while the repaint happens.
Extract the code that builds a `ProbeHintsBuilder` from the environment to a
separate `ProbeHintsBuilder` constructor. This allows callers to re-use the
environment-based `ProbeHintsBuilder`, but override other aspects of
`ProbeHints`, e.g. to disable mouse handling.
When repainting a surface, we optimize for the case where lines are simple text
by combining the the `Change::Text` for the end of the previous line and the
start of the next line into a single `Change`.
The assumption about relying on automatic margins is incorrect. We can't rely
on them, as they might be disabled, and in any case they are no use if the
previous line was shorter than the full width of the screen.
This results in the lines appearing joined together on a single line. This is
evidenced in the existing tests where `"hel\nw"` becomes `"helw"` on a full
repaint.
The solution is to always inject a real CRLF by adding a `CursorPosition` change.
This replaces any CRLF that may have been swallowed by the `Surface` when it
added the original changes.
`input_parser.decode_input_records` might not add anything to the input queue,
e.g. if the input event is one that is being ignored. In this case, we must
loop round and wait again for more input.
Remove the dance for appeasing the borrow checker. The borrow checker can
be appeased by borrowing the `input_queue` field directly.
Application cursor keys are a separate set of encodings that applications can
ask the terminal to produce for cursor keys.
Unfortunately, PuTTY generates these for shift-modified cursor keys. If an
application is to distinguish between normal and shift-modified cursor keys
on PuTTY then it will need to be able to distinguish between normal and
application cursor keys.
Add new `KeyCode` variants for application cursor keys.
Add two new `Change` variants: `ScrollRegionUp` and `ScrollRegionDown`, which
scroll part of the screen up or down by a number of lines.
On Unix, these are implemented using the `change_scroll_region` and
`parm_index`/`parm_rindex` terminfo capabilities if available. If `parm_index`
or `parm_rindex` are not available, but `scroll_forward` or `scroll_reverse`
are, then these are used repeatedly to get the same effect.
On Windows, these are implemented using `ScrollConsoleScreenBuffer`.
If the cursor has been made invisible (by setting the shape to `Hidden`),
then in order for it to be visible when it is reset to the default shape,
it must be made visible. This can be done by rendering the `CursorVisible`
sequence.
Where supported, the `CursorNormal` sequence can be rendered to do
both `CursorVisible` and `ResetCursorStyle` in one go.