terminal_light/
lib.rs

1/*!
2
3This crate answers the question *"Is the terminal dark or light?"*.
4
5It provides
6
7* the background color, either as RGB or ANSI
8* the background color's luma, which varies from 0 (black) to 1 (white)
9
10A use case in a TUI is to determine what set of colors would be most suitable depending on the terminal's background:
11
12```
13let should_use_light_skin = terminal_light::luma()
14    .map_or(false, |luma| luma > 0.6);
15```
16
17If you have very specialized skins, you may choose a more precise switch:
18
19```
20match terminal_light::luma() {
21    Ok(luma) if luma > 0.85 => {
22        // Use a "light mode" skin.
23    }
24    Ok(luma) if luma < 0.2 => {
25        // Use a "dark mode" skin.
26    }
27    _ => {
28        // Either we couldn't determine the mode or it's kind of medium.
29        // We should use an itermediate skin, or one defining the background.
30    }
31}
32```
33
34# Strategies
35
36## `$COLORFGBG` strategy
37
38This environment variable is set by some terminals, like konsole or the rxvt family.
39It can also be set by users.
40Its value is like `15;0` where the second number is the ANSI code for the background color.
41
42Bonus:
43
44* querying an env variable is a fast operation
45
46Malus:
47
48* this env variable isn't always immediately updated when you change the color of the terminal
49* the value isn't precise: `0` is "dark" and `15` is "light" but the real RGB color is uncertain as the low ANSI codes are often modified by the user
50
51## "Dynamic colors" OSC escape sequence strategy
52
53Modern terminals implement this xterm extension: a query making it possible to know the background color as RGB.
54
55Terminal-light sends the query to `stdout`, waits for the answer on `stdin` with a timeout of 20ms, then analyses this answer.
56
57Bonus:
58
59* this works well on all tested linux terminals
60* the value is precise (RGB)
61* the value is up to date when it's available
62
63Malus:
64
65* waiting for stdin with a timeout isn't implemented on Windows in this crate (help welcome)
66* this isn't instant, a delay of 10 ms to get the answer isn't unusual
67* if a not compatible terminal doesn't answer at all, we're waiting for 20ms
68* it may fail on some terminal multiplexers
69
70## Global strategy used by Terminal-light
71
721. if we're on a unix-like platform, we try the escape sequence strategy
732. if it failed or we're not on unix, we try the `$COLORFGBG` strategy
743. without a solution, we return a `TlError::Unsupported` error
75
76*/
77
78pub mod env;
79mod error;
80
81#[cfg(unix)]
82mod xterm;
83
84pub use {coolor::*, error::*};
85
86/// Try to determine the background color of the terminal.
87///
88/// The result may come as Ansi or Rgb, depending on where
89/// the information has been found.
90///
91/// If you want it as RGB:
92///
93/// ```
94/// let backround_color_rgb = terminal_light::background_color()
95///     .map(|c| c.rgb()); // may be an error
96/// ```
97pub fn background_color() -> Result<Color, TlError> {
98    #[cfg(unix)]
99    {
100        let xterm_color = xterm::query_bg_color();
101        if let Ok(xterm_color) = xterm_color {
102            return Ok(Color::Rgb(xterm_color));
103        }
104    }
105    let env_color = env::bg_color();
106    if let Ok(env_color) = env_color {
107        return Ok(Color::Ansi(env_color));
108    }
109    Err(TlError::Unsupported)
110}
111
112/// Try to return the "luma" value of the terminal's background, characterizing
113/// the "light" of the color, going from 0 (black) to 1 (white).
114///
115/// You can say a terminal is "dark" when the luma is below 0.2 and
116/// "light" when it's over 0.9. If you need to choose a pivot between
117/// "rather dark" and "rather light" then 0.6 should do.
118pub fn luma() -> Result<f32, TlError> {
119    background_color().map(|c| c.luma())
120}