2020-05-05 22:50:23 +00:00
|
|
|
//! TTY related functionality.
|
2020-06-06 18:49:14 +00:00
|
|
|
|
2020-12-22 02:38:50 +00:00
|
|
|
use std::path::PathBuf;
|
2018-11-11 12:55:28 +00:00
|
|
|
use std::{env, io};
|
|
|
|
|
2018-12-10 17:53:56 +00:00
|
|
|
use crate::config::Config;
|
2018-10-16 17:02:52 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
mod unix;
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
pub use self::unix::*;
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2020-01-02 11:49:27 +00:00
|
|
|
pub mod windows;
|
2018-10-16 17:02:52 +00:00
|
|
|
#[cfg(windows)]
|
|
|
|
pub use self::windows::*;
|
|
|
|
|
|
|
|
/// This trait defines the behaviour needed to read and/or write to a stream.
|
|
|
|
/// It defines an abstraction over mio's interface in order to allow either one
|
2019-04-28 21:42:43 +00:00
|
|
|
/// read/write object or a separate read and write object.
|
2018-10-16 17:02:52 +00:00
|
|
|
pub trait EventedReadWrite {
|
|
|
|
type Reader: io::Read;
|
|
|
|
type Writer: io::Write;
|
|
|
|
|
2018-11-11 12:55:28 +00:00
|
|
|
fn register(
|
|
|
|
&mut self,
|
2018-12-10 17:53:56 +00:00
|
|
|
_: &mio::Poll,
|
2019-03-12 19:44:47 +00:00
|
|
|
_: &mut dyn Iterator<Item = mio::Token>,
|
2018-12-10 17:53:56 +00:00
|
|
|
_: mio::Ready,
|
|
|
|
_: mio::PollOpt,
|
2018-11-11 12:55:28 +00:00
|
|
|
) -> io::Result<()>;
|
2018-12-10 17:53:56 +00:00
|
|
|
fn reregister(&mut self, _: &mio::Poll, _: mio::Ready, _: mio::PollOpt) -> io::Result<()>;
|
|
|
|
fn deregister(&mut self, _: &mio::Poll) -> io::Result<()>;
|
2018-10-16 17:02:52 +00:00
|
|
|
|
|
|
|
fn reader(&mut self) -> &mut Self::Reader;
|
|
|
|
fn read_token(&self) -> mio::Token;
|
|
|
|
fn writer(&mut self) -> &mut Self::Writer;
|
|
|
|
fn write_token(&self) -> mio::Token;
|
|
|
|
}
|
2018-11-11 12:55:28 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Events concerning TTY child processes.
|
2019-11-16 21:11:56 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2019-03-12 19:44:47 +00:00
|
|
|
pub enum ChildEvent {
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Indicates the child has exited.
|
2019-03-30 16:48:36 +00:00
|
|
|
Exited,
|
2019-03-12 19:44:47 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// A pseudoterminal (or PTY).
|
2019-03-12 19:44:47 +00:00
|
|
|
///
|
|
|
|
/// This is a refinement of EventedReadWrite that also provides a channel through which we can be
|
|
|
|
/// notified if the PTY child process does something we care about (other than writing to the TTY).
|
|
|
|
/// In particular, this allows for race-free child exit notification on UNIX (cf. `SIGCHLD`).
|
2019-03-30 16:48:36 +00:00
|
|
|
pub trait EventedPty: EventedReadWrite {
|
2019-03-12 19:44:47 +00:00
|
|
|
fn child_event_token(&self) -> mio::Token;
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Tries to retrieve an event.
|
2019-03-12 19:44:47 +00:00
|
|
|
///
|
|
|
|
/// Returns `Some(event)` on success, or `None` if there are no events to retrieve.
|
|
|
|
fn next_child_event(&mut self) -> Option<ChildEvent>;
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Setup environment variables.
|
2019-10-05 00:29:26 +00:00
|
|
|
pub fn setup_env<C>(config: &Config<C>) {
|
2018-11-11 12:55:28 +00:00
|
|
|
// Default to 'alacritty' terminfo if it is available, otherwise
|
|
|
|
// default to 'xterm-256color'. May be overridden by user's config
|
|
|
|
// below.
|
2020-12-22 02:38:50 +00:00
|
|
|
let terminfo = if terminfo_exists("alacritty") { "alacritty" } else { "xterm-256color" };
|
|
|
|
env::set_var("TERM", terminfo);
|
2018-11-11 12:55:28 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Advertise 24-bit color support.
|
2018-11-11 12:55:28 +00:00
|
|
|
env::set_var("COLORTERM", "truecolor");
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Prevent child processes from inheriting startup notification env.
|
2019-06-16 16:03:52 +00:00
|
|
|
env::remove_var("DESKTOP_STARTUP_ID");
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Set env vars from config.
|
2019-05-10 11:36:16 +00:00
|
|
|
for (key, value) in config.env.iter() {
|
2018-11-11 12:55:28 +00:00
|
|
|
env::set_var(key, value);
|
|
|
|
}
|
|
|
|
}
|
2020-12-22 02:38:50 +00:00
|
|
|
|
|
|
|
/// Check if a terminfo entry exists on the system.
|
|
|
|
fn terminfo_exists(terminfo: &str) -> bool {
|
|
|
|
// Get first terminfo character for the parent directory.
|
|
|
|
let first = terminfo.get(..1).unwrap_or_default();
|
|
|
|
let first_hex = format!("{:x}", first.chars().next().unwrap_or_default() as usize);
|
|
|
|
|
|
|
|
// Return true if the terminfo file exists at the specified location.
|
|
|
|
macro_rules! check_path {
|
|
|
|
($path:expr) => {
|
|
|
|
if $path.join(first).join(terminfo).exists()
|
|
|
|
|| $path.join(&first_hex).join(terminfo).exists()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(dir) = env::var_os("TERMINFO") {
|
|
|
|
check_path!(PathBuf::from(&dir));
|
|
|
|
} else if let Some(home) = dirs::home_dir() {
|
|
|
|
check_path!(home.join(".terminfo"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(dirs) = env::var("TERMINFO_DIRS") {
|
|
|
|
for dir in dirs.split(':') {
|
|
|
|
check_path!(PathBuf::from(dir));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(prefix) = env::var("PREFIX") {
|
|
|
|
let path = PathBuf::from(prefix);
|
|
|
|
check_path!(path.join("etc/terminfo"));
|
|
|
|
check_path!(path.join("lib/terminfo"));
|
|
|
|
check_path!(path.join("share/terminfo"));
|
|
|
|
}
|
|
|
|
|
|
|
|
check_path!(PathBuf::from("/etc/terminfo"));
|
|
|
|
check_path!(PathBuf::from("/lib/terminfo"));
|
|
|
|
check_path!(PathBuf::from("/usr/share/terminfo"));
|
|
|
|
check_path!(PathBuf::from("/boot/system/data/terminfo"));
|
|
|
|
|
|
|
|
// No valid terminfo path has been found.
|
|
|
|
false
|
|
|
|
}
|