Added color support detection (#91)

* Added color support detection

Color support is inferred by using either OSC 4 escape codes or the
value of TERM.

* minor refactor and cosmetic changes
This commit is contained in:
IGI-111 2017-02-26 09:35:39 +01:00 committed by ticki
parent 23ef0260dd
commit ba72d0bcec
2 changed files with 97 additions and 0 deletions

19
examples/detect_color.rs Normal file
View File

@ -0,0 +1,19 @@
extern crate termion;
use termion::color::{DetectColors, AnsiValue, Bg};
use termion::raw::IntoRawMode;
use std::io::stdout;
fn main() {
let count;
{
let mut term = stdout().into_raw_mode().unwrap();
count = term.available_colors().unwrap();
}
println!("This terminal supports {} colors.", count);
for i in 0..count {
print!("{} {}", Bg(AnsiValue(i as u8)), Bg(AnsiValue(0)));
}
println!();
}

View File

@ -13,6 +13,11 @@
//! ``` //! ```
use std::fmt; use std::fmt;
use raw::RawTerminal;
use std::io::{self, Write, Read};
use std::time::{SystemTime, Duration};
use async::async_stdin;
use std::env;
/// A terminal color. /// A terminal color.
pub trait Color { pub trait Color {
@ -160,3 +165,76 @@ impl<C: Color> fmt::Display for Bg<C> {
self.0.write_bg(f) self.0.write_bg(f)
} }
} }
/// Types that allow detection of the colors they support.
pub trait DetectColors {
/// How many ANSI colors are supported (from 8 to 256)?
///
/// Beware: the information given isn't authoritative, it's infered through escape codes or the
/// value of `TERM`, more colors may be available.
fn available_colors(&mut self) -> io::Result<u16>;
}
impl<W: Write> DetectColors for RawTerminal<W> {
fn available_colors(&mut self) -> io::Result<u16> {
let mut stdin = async_stdin();
if detect_color(self, &mut stdin, 0)? {
// OSC 4 is supported, detect how many colors there are.
// Do a binary search of the last supported color.
let mut min = 8;
let mut max = 256;
let mut i;
while min + 1 < max {
i = (min + max) / 2;
if detect_color(self, &mut stdin, i)? {
min = i
} else {
max = i
}
}
Ok(max)
} else {
// OSC 4 is not supported, trust TERM contents.
Ok(match env::var_os("TERM") {
Some(val) => {
if val.to_str().unwrap_or("").contains("256color") {
256
} else {
8
}
}
None => 8,
})
}
}
}
/// The timeout of an escape code control sequence, in milliseconds.
const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
/// Detect a color using OSC 4.
fn detect_color<W: Write>(stdout: &mut RawTerminal<W>,
stdin: &mut Read,
color: u16)
-> io::Result<bool> {
// Is the color available?
// Use `ESC ] 4 ; color ; ? BEL`.
write!(stdout, "\x1B]4;{};?\x07", color)?;
stdout.flush()?;
let mut buf: [u8; 1] = [0];
let mut total_read = 0;
let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
let now = SystemTime::now();
let bell = 7u8;
// Either consume all data up to bell or wait for a timeout.
while buf[0] != bell && now.elapsed().unwrap() < timeout {
total_read += stdin.read(&mut buf)?;
}
// If there was a response, the color is supported.
Ok(total_read > 0)
}