termion/
color.rs

1//! Color managemement.
2//!
3//! # Example
4//!
5//! ```rust
6//! use termion::color;
7//!
8//! fn main() {
9//!     println!("{}Red", color::Fg(color::Red));
10//!     println!("{}Blue", color::Fg(color::Blue));
11//!     println!("{}Back again", color::Fg(color::Reset));
12//! }
13//! ```
14
15use std::fmt;
16use raw::CONTROL_SEQUENCE_TIMEOUT;
17use std::io::{self, Write, Read};
18use std::time::{SystemTime, Duration};
19use async::async_stdin;
20use std::env;
21use std::fmt::Debug;
22use numtoa::NumToA;
23
24/// A terminal color.
25pub trait Color: Debug {
26    /// Write the foreground version of this color.
27    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
28    /// Write the background version of this color.
29    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
30}
31
32macro_rules! derive_color {
33    ($doc:expr, $name:ident, $value:expr) => {
34        #[doc = $doc]
35        #[derive(Copy, Clone, Debug)]
36        pub struct $name;
37
38        impl Color for $name {
39            #[inline]
40            fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
41                f.write_str(self.fg_str())
42            }
43
44            #[inline]
45            fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
46                f.write_str(self.bg_str())
47            }
48        }
49
50        impl $name {
51            #[inline]
52            /// Returns the ANSI escape sequence as a string.
53            pub fn fg_str(&self) -> &'static str { csi!("38;5;", $value, "m") }
54
55            #[inline]
56            /// Returns the ANSI escape sequences as a string.
57            pub fn bg_str(&self) -> &'static str { csi!("48;5;", $value, "m") }
58        }
59    };
60}
61
62derive_color!("Black.", Black, "0");
63derive_color!("Red.", Red, "1");
64derive_color!("Green.", Green, "2");
65derive_color!("Yellow.", Yellow, "3");
66derive_color!("Blue.", Blue, "4");
67derive_color!("Magenta.", Magenta, "5");
68derive_color!("Cyan.", Cyan, "6");
69derive_color!("White.", White, "7");
70derive_color!("High-intensity light black.", LightBlack, "8");
71derive_color!("High-intensity light red.", LightRed, "9");
72derive_color!("High-intensity light green.", LightGreen, "10");
73derive_color!("High-intensity light yellow.", LightYellow, "11");
74derive_color!("High-intensity light blue.", LightBlue, "12");
75derive_color!("High-intensity light magenta.", LightMagenta, "13");
76derive_color!("High-intensity light cyan.", LightCyan, "14");
77derive_color!("High-intensity light white.", LightWhite, "15");
78
79impl<'a> Color for &'a Color {
80    #[inline]
81    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
82        (*self).write_fg(f)
83    }
84
85    #[inline]
86    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
87        (*self).write_bg(f)
88    }
89}
90
91/// An arbitrary ANSI color value.
92#[derive(Clone, Copy, Debug)]
93pub struct AnsiValue(pub u8);
94
95impl AnsiValue {
96    /// 216-color (r, g, b ≤ 5) RGB.
97    pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
98        debug_assert!(r <= 5,
99                      "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
100                      r);
101        debug_assert!(g <= 5,
102                      "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
103                      g);
104        debug_assert!(b <= 5,
105                      "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
106                      b);
107
108        AnsiValue(16 + 36 * r + 6 * g + b)
109    }
110
111    /// Grayscale color.
112    ///
113    /// There are 24 shades of gray.
114    pub fn grayscale(shade: u8) -> AnsiValue {
115        // Unfortunately, there are a little less than fifty shades.
116        debug_assert!(shade < 24,
117                      "Grayscale out of bound (shade = {}). There are only 24 shades of \
118                      gray.",
119                      shade);
120
121        AnsiValue(0xE8 + shade)
122    }
123}
124
125impl AnsiValue {
126    /// Returns the ANSI sequence as a string.
127    pub fn fg_string(self) -> String {
128        let mut x = [0u8; 20];
129        let x = self.0.numtoa_str(10, &mut x);
130        [csi!("38;5;"), x, "m"].concat()
131    }
132
133    /// Returns the ANSI sequence as a string.
134    pub fn bg_string(self) -> String {
135        let mut x = [0u8; 20];
136        let x = self.0.numtoa_str(10, &mut x);
137        [csi!("48;5;"), x, "m"].concat()
138    }
139}
140
141impl Color for AnsiValue {
142    #[inline]
143    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        f.write_str(&self.fg_string())
145    }
146
147    #[inline]
148    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        f.write_str(&self.bg_string())
150    }
151}
152
153/// A truecolor RGB.
154#[derive(Debug, Clone, Copy, PartialEq)]
155pub struct Rgb(pub u8, pub u8, pub u8);
156
157impl Rgb {
158    /// Returns the ANSI sequence as a string.
159    pub fn fg_string(self) -> String {
160        let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
161        let (x, y, z) = (
162            self.0.numtoa_str(10, &mut x),
163            self.1.numtoa_str(10, &mut y),
164            self.2.numtoa_str(10, &mut z),
165        );
166
167        [csi!("38;2;"), x, ";", y, ";", z, "m"].concat()
168    }
169
170    /// Returns the ANSI sequence as a string.
171    pub fn bg_string(self) -> String {
172        let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
173        let (x, y, z) = (
174            self.0.numtoa_str(10, &mut x),
175            self.1.numtoa_str(10, &mut y),
176            self.2.numtoa_str(10, &mut z),
177        );
178
179        [csi!("48;2;"), x, ";", y, ";", z, "m"].concat()
180    }
181}
182
183impl Color for Rgb {
184    #[inline]
185    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        f.write_str(&self.fg_string())
187    }
188
189    #[inline]
190    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        f.write_str(&self.bg_string())
192    }
193}
194
195/// Reset colors to defaults.
196#[derive(Debug, Clone, Copy)]
197pub struct Reset;
198
199const RESET_FG: &str = csi!("39m");
200const RESET_BG: &str = csi!("49m");
201
202impl Reset {
203    /// Returns the ANSI sequence as a string.
204    pub fn fg_str(self) -> &'static str { RESET_FG }
205    /// Returns the ANSI sequence as a string.
206    pub fn bg_str(self) -> &'static str { RESET_BG }
207}
208
209impl Color for Reset {
210    #[inline]
211    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        f.write_str(RESET_FG)
213    }
214
215    #[inline]
216    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
217        f.write_str(RESET_BG)
218    }
219}
220
221/// A foreground color.
222#[derive(Debug, Clone, Copy)]
223pub struct Fg<C: Color>(pub C);
224
225impl<C: Color> fmt::Display for Fg<C> {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        self.0.write_fg(f)
228    }
229}
230
231/// A background color.
232#[derive(Debug, Clone, Copy)]
233pub struct Bg<C: Color>(pub C);
234
235impl<C: Color> fmt::Display for Bg<C> {
236    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237        self.0.write_bg(f)
238    }
239}
240
241/// Types that allow detection of the colors they support.
242pub trait DetectColors {
243    /// How many ANSI colors are supported (from 8 to 256)?
244    ///
245    /// Beware: the information given isn't authoritative, it's infered through escape codes or the
246    /// value of `TERM`, more colors may be available.
247    fn available_colors(&mut self) -> io::Result<u16>;
248}
249
250impl<W: Write> DetectColors for W {
251    fn available_colors(&mut self) -> io::Result<u16> {
252        let mut stdin = async_stdin();
253
254        if detect_color(self, &mut stdin, 0)? {
255            // OSC 4 is supported, detect how many colors there are.
256            // Do a binary search of the last supported color.
257            let mut min = 8;
258            let mut max = 256;
259            let mut i;
260            while min + 1 < max {
261                i = (min + max) / 2;
262                if detect_color(self, &mut stdin, i)? {
263                    min = i
264                } else {
265                    max = i
266                }
267            }
268            Ok(max)
269        } else {
270            // OSC 4 is not supported, trust TERM contents.
271            Ok(match env::var_os("TERM") {
272                   Some(val) => {
273                       if val.to_str().unwrap_or("").contains("256color") {
274                           256
275                       } else {
276                           8
277                       }
278                   }
279                   None => 8,
280               })
281        }
282    }
283}
284
285/// Detect a color using OSC 4.
286fn detect_color(stdout: &mut Write, stdin: &mut Read, color: u16) -> io::Result<bool> {
287    // Is the color available?
288    // Use `ESC ] 4 ; color ; ? BEL`.
289    write!(stdout, "\x1B]4;{};?\x07", color)?;
290    stdout.flush()?;
291
292    let mut buf: [u8; 1] = [0];
293    let mut total_read = 0;
294
295    let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
296    let now = SystemTime::now();
297    let bell = 7u8;
298
299    // Either consume all data up to bell or wait for a timeout.
300    while buf[0] != bell && now.elapsed().unwrap() < timeout {
301        total_read += stdin.read(&mut buf)?;
302    }
303
304    // If there was a response, the color is supported.
305    Ok(total_read > 0)
306}