A user reported that ctrl-c and ctrl-\ had no effect
for them in bash when spawned in a particular way
on their system. It turned out that the spawning
environment had blocked SIGINT, SIGHUP and SIGQUIT
and that was propagated all the way down through
the wezterm process to the spawned shell.
Let's ensure that we clear all blocked signals
prior to launching our child process.
The tcgetpgrp call appears to have high variance in latency, ranging
from 200-700us on my system.
If you have 10 tabs and mouse over the tab bar, that's around 7ms
spent per frame just figuring out the foreground process; that doesn't
include actually extracting the process executable or current working
directory paths.
This was exacerbated by the mouse move events triggering a tab bar
recompute on every pixel of mouse movement.
This commit takes the following steps to resolve this:
* We now only re-compute the tab bar when the UI item is changed by
a mouse movement
* A simple single-item cache is now used on unix that allows the caller
to proceed quickly with stale-but-probably-still-mostly-accurate data
while queuing up an update to a background thread which can absorb
the latency.
The result of this is that hovering over several tabs in quick
succession no longer takes a noticeable length of time to render the
hover, but the consequence is that the contents of a given tab may be
stale by 300-400ms.
I think that trade-off is worth while.
We already have a similar trade-off on Windows, although we don't
yet do the updates in a different thread on Windows. Perhaps in
a follow up commit?
refs: https://github.com/wez/wezterm/issues/2991
ad9490ee8f unset SHELL from the
environment on startup which had the consequence of causing
`os.getenv("SHELL")` to return `nil` when used in the config file.
Rather than simply restoring SHELL env var, recognize that reading
the environment from a long lived process is prone to seeing
stable environment forever.
We already compensate for this in the pty crate's understanding
of the base environment, so this commit patches `os.getenv`
and replaces it with our own imlementation that uses that same
logic.
The base environment logic has been extended to set SHELL from
the passwd database to round things out.
refs: https://github.com/wez/wezterm/discussions/2481
There are caveats to determining this, but when we think
password entry is enabled, switch the cursor to the font-awesome
lock glyph instead of the normal cursor sprite.
fa_lock is used because it is monochrome and can thus be tinted
to the configured cursor color, and it respects blinking/easing.
refs: https://github.com/wez/wezterm/issues/2460
e6421d1b72 removed this bit from the
example:
```
-
- // Note that we're waiting until after we've read the output
- // to call `wait` on the process.
- // On macOS Catalina, waiting on the process seems to prevent
- // its output from making it into the pty.
- println!("child status: {:?}", child.wait().unwrap());
```
This commit revisits that and puts in place a differently horrible
solution for macos.
The issue is that if we don't put in a short sleep on macos, then
for a short lived process like `whoami` in this example, we end up
reading output like this:
```
; cargo run --example whoami
Compiling portable-pty v0.8.0 (/Users/wez/wez-personal/wezterm/pty)
Finished dev [unoptimized + debuginfo] target(s) in 0.60s
Running `/Users/wez/wez-personal/wezterm/target/debug/examples/whoami`
child status: ExitStatus { code: 0, signal: None }
output: \r\n^D\u{8}\u{8}wez\r\n%
```
where the EOT that we send on Drop somehow gets *prepended* to the output
that we read from the pty with a couple of BELs thrown in.
I'm not sure WTF is happening on macOS for that to occur; feels like
some kind of race wrt. process startup and initializing the pty in the
system.
The reader has to be started before we close it as well, otherwise
the same issue can occur.
This breaking API change allows us to explicitly generate EOF when the
taken writer is dropped.
The examples have been updated to show how to manage read, write
and waiting without deadlock for both linux and windows.
Need to confirm that this is still good on macOS, but my
confidence is high.
I've also removed ssh2 support from this crate as part of this
change. We haven't used it directly in wezterm in a long while
and removing it from here means that there is slightly less code
to keep compiling over and over.
refs: https://github.com/wez/wezterm/discussions/2392
refs: https://github.com/wez/wezterm/issues/1396
`wezterm start -- /etc/profile` would crash on macOS because
`/etc/profile` isn't executable.
This commit checks for executable access as a prereq for both the
path search and the absolute path cases and generates a non-crashing
error:
```
$ wezterm start -- /etc/profile
17:24:03.574 ERROR wezterm_gui > Unable to spawn /etc/profile because it doesn't exist on the filesystem or is not executable (EACCES: Permission denied); terminating
```
This commit allows wezterm to spawn programs into the host rather
than in the container environment.
It feels weird that it is so trivial to "break out" of the container
sandbox, but I'm not complaining.
There are some unfortunate consequences:
* there is no `wezterm` installed on the host, so no ability to `wezterm
cli` to control it from other apps
* The unix domain socket is scoped inside the sandbox, so there's "no
way" for `wezterm cli` to reach inside anyway.
But: with this, it is at least usable to start a flatpak and open a
shell.
refs: https://github.com/wez/wezterm/issues/2229
An ExecDomain is a variation on WslDomain with the key difference
being that you can control how to map the command that would be
executed.
The idea is that the user can define eg: a domain for a docker
container, or a domain that chooses to run every command in its
own cgroup.
The example below shows a really crappy implementation as a
demonstration:
```
local wezterm = require 'wezterm'
return {
exec_domains = {
-- Commands executed in the woot domain have "WOOT" echoed
-- first and are then run via bash.
-- `cmd` is a SpawnCommand
wezterm.exec_domain("woot", function(cmd)
if cmd.args then
cmd.args = {
"bash",
"-c",
"echo WOOT && " .. wezterm.shell_join_args(cmd.args)
}
end
-- you must return the SpawnCommand that will be run
return cmd
end),
},
default_domain = "woot",
}
```
This commit unfortunately does more than should go into a single
commit, but I'm a bit too lazy to wrangle splitting it up.
* Reverts the nil/null stuff from #2177 and makes the
`ExtendSelectionToMouseCursor` parameter mandatory to dodge
a whole load of urgh around nil in table values. That is
necessary because SpawnCommand uses optional fields and the
userdata proxy was making that a PITA.
* Adds some shell quoting helper functions
* Adds ExecDomain itself, which is really just a way to
to run a callback to fixup the command that will be run.
That command is converted to a SpawnCommand for the callback
to process in lua and return an adjusted version of it,
then converted back to a command builder for execution.
refs: https://github.com/wez/wezterm/issues/1776
I think I'd like to make a config option for this, but for the moment,
this first pass unconditionally updates the base environment with
data from the registry.
refs: https://github.com/wez/wezterm/issues/1848
The serial port is always in non-blocking mode so we need to use our
own timeout and `poll(2)`.
While we're in here, the `wait` method could cause the gui to exit
immediately on startup because we'd interpret its immediate error
return result as child process completion.
This commit makes the wait method look at the carrier detect signal
and propagates IO errors back as completion events.
refs: https://github.com/wez/wezterm/issues/1594
The idea is that we want to be able to spawn into wsl with the
convenience of a local domain, but without the awkwardness of
it having a different filesystem namespace.
It would also be great to be able to spawn a new tab or pane
in the same domain and pick up the cwd of the existing one.
The WslDomain allows the user to explicitly list WslDomains
and control eg: default shell, username and so on, but wezterm
will pre-fill a default list of domains based on the `wsl -l`
output that we were already using in the launcher menu.
The existing LocalDomain has been augmented to understand that
it may need to fixup a command invocation and that gives it
the opportunity to rewrite the command so that we can launch
it via `wsl.exe` and pass down the cwd and so on.
This same technique might be extensible to eg: docker instances
in the future.
This commit:
* Introduces `wsl_domains` config and its default list of wsl
distributions
* Creates LocalDomain instances from that list
* The launcher menu allows spawning a new tab via one of those domains
ConPTY emits a sequence that sets the title to the name of the
program that is initially launched into it.
This commit tries to ignore that sequence in that circumstance,
so that the logic in b5d156c282
can more dynamically set the tab title.
For some reason, winapi's HANDLE type isn't compatible with the core
rust ffi HANDLE type when cross compiling.
This commit adds some casts.
```
cd pty
cargo build --release --target x86_64-pc-windows-msvc
```
refs: #1389
In the mux layer, we have some code that takes a `Child` and then
does a bit of naughty reaching through the abstraction to get at
the pid/handle of the child so that we can send it signals even
if the child is itself mutably (and thus exclusively) borrowed
for the purposes of waiting.
That worked fine for local processes spawned in the mux, but we also
use LocalPane to wrap around arbitrary `Child`ren, such as Ssh,
that are not local and that don't have a local process id, which
meant that this hack wouldn't work for them.
To make things a bit worse, those ssh ptys were used to ssh2 days
where we didn't have a way to signal the remote process and just
did nothing, leading to confusing situations such as
https://github.com/wez/wezterm/issues/1197
This commit graduates the hack mentioned in the first paragraph
to its own ChildKiller trait. This makes the concept of waiting
for the Child distinct from signalling it and explicitly allows
getting a separate object that can be used for signalling.
With that in place, we're forced to implement something appropriate
for the ssh pty implementations; one in the pty crate itself,
one in wezterm-ssh and the wrapper that we use in the mux crate.
The upshot of this is that the `CloseCurrentPane` action now operates
correctly on panes that were the result of split operations.
This got lost somewhere along the way.
Importantly, we need to explicitly set the pty to use invalid stdio
handles for the spawned child in order to avoid a weird situation
where eg: cmd or powershell would end up writing to the log file
despite it being spawned into its own PTY.
refs: #1358
Without this check, something, presumably in the guts of rust itself,
generates `fatal runtime error: assertion failed:
output.write(&bytes).is_ok()` on the output stream of the spawned child
when attempting to spawn `wsl.exe` on linux.
refs: #1005
Looking at #900; the unconditional directory change on startup
is "bad" because it only happens on Windows.
This commit removes it and instead puts the logic into the pty
layer to match the unix behavior.
The behavior is:
* If the command specifies the cwd, use that.
* Otherwise, use the home directory
Since removing the regular periodic background tasks, we're now
prone to not noticing child processes exiting.
This commit explicictly schedules a thread to do that on Windows
so that we can close a tab as soon as it exits.
Since the original code was written, Rust grew and stabilized
an interface for specifying argv0 for the child process, so
we can remove the fragile `-l` argument passing.
refs: https://github.com/wez/wezterm/issues/753