liso 1.3.3

Line Input with Simultaneous Output: input lines are editable, output lines are never scrambled, and all of it thread safe.
Documentation
use super::*;

use std::io::{Read, Stdout};

mod ansi;
use ansi::AnsiTerminal;
mod vt52;
use vt52::Vt52;

#[cfg(all(not(unix), windows))]
mod win;
#[cfg(all(not(unix), windows))]
use win::WinTerminal;

/// A wrapper for a particular terminal engine, supporting input and output.
///
/// We decide which engine to use based on the environment.
pub(crate) trait Term {
    /// Set the current attributes. (If possible, defer the actual control
    /// code outputting until the next time one of the `print_*` functions is
    /// called.)
    fn set_attrs(
        &mut self,
        style: Style,
        fg: Option<Color>,
        bg: Option<Color>,
    ) -> LifeOrDeath;
    fn reset_attrs(&mut self) -> LifeOrDeath;
    fn print(&mut self, text: &str) -> LifeOrDeath;
    fn print_char(&mut self, char: char) -> LifeOrDeath;
    fn print_spaces(&mut self, spaces: usize) -> LifeOrDeath;
    fn move_cursor_up(&mut self, amt: u32) -> LifeOrDeath;
    fn move_cursor_down(&mut self, amt: u32) -> LifeOrDeath;
    fn move_cursor_left(&mut self, amt: u32) -> LifeOrDeath;
    fn move_cursor_right(&mut self, amt: u32) -> LifeOrDeath;
    fn newline(&mut self) -> LifeOrDeath;
    fn carriage_return(&mut self) -> LifeOrDeath;
    fn bell(&mut self) -> LifeOrDeath;
    fn clear_all_and_reset(&mut self) -> LifeOrDeath;
    fn clear_forward_and_reset(&mut self) -> LifeOrDeath;
    fn clear_to_end_of_line(&mut self) -> LifeOrDeath;
    fn hide_cursor(&mut self) -> LifeOrDeath;
    fn show_cursor(&mut self) -> LifeOrDeath;
    fn get_width(&mut self) -> u32;
    fn cur_style(&self) -> Style;
    fn flush(&mut self) -> LifeOrDeath;
    fn suspend(&mut self) -> LifeOrDeath;
    fn unsuspend(&mut self) -> LifeOrDeath;
    fn cleanup(&mut self) -> LifeOrDeath;
}

pub(crate) fn new_term(
    req_tx: &std_mpsc::Sender<Request>,
) -> Result<Box<dyn Term>, DummyError> {
    if let Ok(term) = std::env::var("TERM") {
        let main = term.split('-').next().unwrap_or("");
        match main {
            "st52" | "tw52" | "tt52" | "at" | "atari" | "atarist"
            | "atari_st" | "vt52" | "stv52" | "stv52pc" => {
                // A real VT52, or (way more likely) an Atari ST (or
                // descendant) emulating one
                let monochrome = main == "vt52" || term.ends_with("-m");
                let num_colors = if monochrome {
                    2
                } else if main.starts_with("st") {
                    // When it's an Atari ST, there are three possibilities
                    // - 80 x 50: high res = monochrome
                    // - 80 x 25: medium res = 4 colors
                    // - 40 x 25: low res = 16 colors
                    // Anything else is a misconfiguration, so we just
                    // assume monochrome to be safe.
                    match termsize::get()
                        .map(|x| (x.cols as u32, x.rows as u32))
                        .unwrap_or((80, 50))
                    {
                        (80, 50) => 2,
                        (80, 25) => 4,
                        (40, 25) => 16,
                        _ => {
                            eprintln!(
                                "Your terminal is configured \
                                           incorrectly. Assuming monochrome."
                            );
                            2
                        }
                    }
                } else {
                    16
                };
                return Ok(Box::new(Vt52::new(req_tx.clone(), num_colors)?));
            }
            _ => (), // fall through
        }
    }
    #[cfg(all(not(unix), windows))]
    if std::env::var("TERM").is_err() {
        return Ok(Box::new(WinTerminal::new(req_tx.clone())?));
    }
    Ok(Box::new(AnsiTerminal::new(req_tx.clone())?))
}