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 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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue