mirror of
https://github.com/wez/wezterm.git
synced 2024-12-04 17:45:23 +03:00
e6421d1b72
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
82 lines
2.9 KiB
Rust
82 lines
2.9 KiB
Rust
//! This is a conceptually simple example that spawns the `whoami` program
|
|
//! to print your username. It is made more complex because there are multiple
|
|
//! pipes involved and it is easy to get blocked/deadlocked if care and attention
|
|
//! is not paid to those pipes!
|
|
use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem};
|
|
use std::sync::mpsc::channel;
|
|
|
|
fn main() {
|
|
let pty_system = NativePtySystem::default();
|
|
|
|
let pair = pty_system
|
|
.openpty(PtySize {
|
|
rows: 24,
|
|
cols: 80,
|
|
pixel_width: 0,
|
|
pixel_height: 0,
|
|
})
|
|
.unwrap();
|
|
|
|
let cmd = CommandBuilder::new("whoami");
|
|
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
|
|
|
// Release any handles owned by the slave: we don't need it now
|
|
// that we've spawned the child.
|
|
drop(pair.slave);
|
|
|
|
{
|
|
// Obtain the writer.
|
|
// When the writer is dropped, EOF will be sent to
|
|
// the program that was spawned.
|
|
// It is important to take the writer even if you don't
|
|
// send anything to its stdin so that EOF can be
|
|
// generated, otherwise you risk deadlocking yourself.
|
|
let mut writer = pair.master.take_writer().unwrap();
|
|
|
|
// This example doesn't need to write anything, but if you
|
|
// want to send data to the child, you'd set `to_write` to
|
|
// that data and do it like this:
|
|
let to_write = "";
|
|
if !to_write.is_empty() {
|
|
// To avoid deadlock, wrt. reading and waiting, we send
|
|
// data to the stdin of the child in a different thread.
|
|
std::thread::spawn(move || {
|
|
writer.write_all(to_write.as_bytes()).unwrap();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Read the output in another thread.
|
|
// This is important because it is easy to encounter a situation
|
|
// where read/write buffers fill and block either your process
|
|
// or the spawned process.
|
|
let (tx, rx) = channel();
|
|
let mut reader = pair.master.try_clone_reader().unwrap();
|
|
std::thread::spawn(move || {
|
|
// Consume the output from the child
|
|
let mut s = String::new();
|
|
reader.read_to_string(&mut s).unwrap();
|
|
tx.send(s).unwrap();
|
|
});
|
|
|
|
// Wait for the child to complete
|
|
println!("child status: {:?}", child.wait().unwrap());
|
|
|
|
// Take care to drop the master after our processes are
|
|
// done, as some platforms get unhappy if it is dropped
|
|
// sooner than that.
|
|
drop(pair.master);
|
|
|
|
// Now wait for the output to be read by our reader thread
|
|
let output = rx.recv().unwrap();
|
|
|
|
// We print with escapes escaped because the windows conpty
|
|
// implementation synthesizes title change escape sequences
|
|
// in the output stream and it can be confusing to see those
|
|
// printed out raw in another terminal.
|
|
print!("output: ");
|
|
for c in output.escape_debug() {
|
|
print!("{}", c);
|
|
}
|
|
}
|