| //! TTY related functionality. |
| |
| use std::path::PathBuf; |
| use std::{env, io}; |
| |
| use crate::config::Config; |
| |
| #[cfg(not(windows))] |
| mod unix; |
| #[cfg(not(windows))] |
| pub use self::unix::*; |
| |
| #[cfg(windows)] |
| pub mod windows; |
| #[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 |
| /// read/write object or a separate read and write object. |
| pub trait EventedReadWrite { |
| type Reader: io::Read; |
| type Writer: io::Write; |
| |
| fn register( |
| &mut self, |
| _: &mio::Poll, |
| _: &mut dyn Iterator<Item = mio::Token>, |
| _: mio::Ready, |
| _: mio::PollOpt, |
| ) -> io::Result<()>; |
| fn reregister(&mut self, _: &mio::Poll, _: mio::Ready, _: mio::PollOpt) -> io::Result<()>; |
| fn deregister(&mut self, _: &mio::Poll) -> io::Result<()>; |
| |
| 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; |
| } |
| |
| /// Events concerning TTY child processes. |
| #[derive(Debug, PartialEq)] |
| pub enum ChildEvent { |
| /// Indicates the child has exited. |
| Exited, |
| } |
| |
| /// A pseudoterminal (or PTY). |
| /// |
| /// 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`). |
| pub trait EventedPty: EventedReadWrite { |
| fn child_event_token(&self) -> mio::Token; |
| |
| /// Tries to retrieve an event. |
| /// |
| /// Returns `Some(event)` on success, or `None` if there are no events to retrieve. |
| fn next_child_event(&mut self) -> Option<ChildEvent>; |
| } |
| |
| /// Setup environment variables. |
| pub fn setup_env<C>(config: &Config<C>) { |
| // Default to 'alacritty' terminfo if it is available, otherwise |
| // default to 'xterm-256color'. May be overridden by user's config |
| // below. |
| let terminfo = if terminfo_exists("alacritty") { "alacritty" } else { "xterm-256color" }; |
| env::set_var("TERM", terminfo); |
| |
| // Advertise 24-bit color support. |
| env::set_var("COLORTERM", "truecolor"); |
| |
| // Prevent child processes from inheriting startup notification env. |
| env::remove_var("DESKTOP_STARTUP_ID"); |
| |
| // Set env vars from config. |
| for (key, value) in config.env.iter() { |
| env::set_var(key, value); |
| } |
| } |
| |
| /// 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 |
| } |