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:
parent
23ef0260dd
commit
ba72d0bcec
|
@ -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!();
|
||||
}
|
78
src/color.rs
78
src/color.rs
|
@ -13,6 +13,11 @@
|
|||
//! ```
|
||||
|
||||
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.
|
||||
pub trait Color {
|
||||
|
@ -160,3 +165,76 @@ impl<C: Color> fmt::Display for Bg<C> {
|
|||
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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue