cursive/backends/curses/
mod.rs

1//! Common module for the ncurses and pancurses backends.
2//!
3//! Requires either of `ncurses-backend` or `pancurses-backend`.
4#![cfg(any(feature = "ncurses-backend", feature = "pancurses-backend"))]
5
6use crate::event::{Event, Key};
7use crate::theme::{BaseColor, Color, ColorPair};
8use maplit::hashmap;
9
10pub mod n;
11pub mod pan;
12
13// Use AHash instead of the slower SipHash
14type HashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
15
16/// Split a i32 into individual bytes, little endian (least significant byte first).
17fn split_i32(code: i32) -> Vec<u8> {
18    (0..4).map(|i| ((code >> (8 * i)) & 0xFF) as u8).collect()
19}
20
21fn fill_key_codes<F>(target: &mut HashMap<i32, Event>, f: F)
22where
23    F: Fn(i32) -> Option<String>,
24{
25    let key_names = hashmap! {
26        "DC" => Key::Del,
27        "DN" => Key::Down,
28        "END" => Key::End,
29        "HOM" => Key::Home,
30        "IC" => Key::Ins,
31        "LFT" => Key::Left,
32        "NXT" => Key::PageDown,
33        "PRV" => Key::PageUp,
34        "RIT" => Key::Right,
35        "UP" => Key::Up,
36    };
37
38    for code in 512..1024 {
39        let name = match f(code) {
40            Some(name) => name,
41            None => continue,
42        };
43
44        if !name.starts_with('k') {
45            continue;
46        }
47
48        let (key_name, modifier) = name[1..].split_at(name.len() - 2);
49        let key = match key_names.get(key_name) {
50            Some(&key) => key,
51            None => continue,
52        };
53        let event = match modifier {
54            "3" => Event::Alt(key),
55            "4" => Event::AltShift(key),
56            "5" => Event::Ctrl(key),
57            "6" => Event::CtrlShift(key),
58            "7" => Event::CtrlAlt(key),
59            _ => continue,
60        };
61        target.insert(code, event);
62    }
63}
64
65fn find_closest_pair(pair: ColorPair, max_colors: i16) -> (i16, i16) {
66    (
67        find_closest(pair.front, max_colors),
68        find_closest(pair.back, max_colors),
69    )
70}
71
72/// Finds the closest index in the 256-color palette.
73///
74/// If `max_colors` is less than 256 (like 8 or 16), the color will be
75/// downgraded to the closest one available.
76fn find_closest(color: Color, max_colors: i16) -> i16 {
77    let max_colors = std::cmp::max(max_colors, 8);
78    match color {
79        Color::TerminalDefault => -1,
80        Color::Dark(BaseColor::Black) => 0,
81        Color::Dark(BaseColor::Red) => 1,
82        Color::Dark(BaseColor::Green) => 2,
83        Color::Dark(BaseColor::Yellow) => 3,
84        Color::Dark(BaseColor::Blue) => 4,
85        Color::Dark(BaseColor::Magenta) => 5,
86        Color::Dark(BaseColor::Cyan) => 6,
87        Color::Dark(BaseColor::White) => 7,
88        Color::Light(BaseColor::Black) => 8 % max_colors,
89        Color::Light(BaseColor::Red) => 9 % max_colors,
90        Color::Light(BaseColor::Green) => 10 % max_colors,
91        Color::Light(BaseColor::Yellow) => 11 % max_colors,
92        Color::Light(BaseColor::Blue) => 12 % max_colors,
93        Color::Light(BaseColor::Magenta) => 13 % max_colors,
94        Color::Light(BaseColor::Cyan) => 14 % max_colors,
95        Color::Light(BaseColor::White) => 15 % max_colors,
96        Color::Rgb(r, g, b) if max_colors >= 256 => {
97            // If r = g = b, it may be a grayscale value!
98            // Grayscale colors have a bit higher resolution than the rest of
99            // the palette, so if we can use it we should!
100            //
101            // r=g=b < 8 should go to pure black instead.
102            // r=g=b >= 247 should go to pure white.
103
104            // TODO: project almost-gray colors as well?
105            if r == g && g == b && (8..247).contains(&r) {
106                // The grayscale palette says the colors 232+n are:
107                // (r = g = b) = 8 + 10 * n
108                // With 0 <= n <= 23. This gives:
109                // (r - 8) / 10 = n
110                let n = (r - 8) / 10;
111                i16::from(232 + n)
112            } else {
113                // Generic RGB
114                let r = 6 * u16::from(r) / 256;
115                let g = 6 * u16::from(g) / 256;
116                let b = 6 * u16::from(b) / 256;
117                (16 + 36 * r + 6 * g + b) as i16
118            }
119        }
120        Color::Rgb(r, g, b) => {
121            // Have to hack it down to 8 colors.
122            let r = i16::from(r > 127);
123            let g = i16::from(g > 127);
124            let b = i16::from(b > 127);
125            r + 2 * g + 4 * b
126        }
127        Color::RgbLowRes(r, g, b) if max_colors >= 256 => i16::from(16 + 36 * r + 6 * g + b),
128        Color::RgbLowRes(r, g, b) => {
129            let r = i16::from(r > 2);
130            let g = i16::from(g > 2);
131            let b = i16::from(b > 2);
132            r + 2 * g + 4 * b
133        }
134    }
135}