use crate::{Color, Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent};
use crosscurses::{mmask_t, Input};
use std::io::Write;
impl<W: Write> super::BackendImpl<W> {
pub fn parse_next(&self, input: crosscurses::Input) -> Event {
let key_event = self.try_parse_key(input).map_or(
self.try_map_shift_key(input).map_or(
self.try_map_ctrl_key(input)
.map_or(self.try_map_ctrl_alt_key(input), Some),
Some,
),
Some,
);
match key_event {
Some(key_event) => Event::Key(key_event),
None => {
self.try_map_non_key_event(input)
.map_or(Event::Unknown, |e| e)
}
}
}
pub fn try_parse_key(&self, input: crosscurses::Input) -> Option<KeyEvent> {
let empty = KeyModifiers::empty();
let key_code = match input {
Input::Character(c) => match c {
'\r' | '\n' => Some(KeyCode::Enter.into()),
'\t' => Some(KeyCode::Tab.into()),
'\x7F' => Some(KeyCode::Backspace.into()),
'\u{8}' => Some(KeyCode::Backspace.into()),
c @ '\x01'..='\x1A' => Some(KeyEvent::new(
KeyCode::Char((c as u8 - 0x1 + b'a') as char),
KeyModifiers::CONTROL,
)),
c @ '\x1C'..='\x1F' => Some(KeyEvent::new(
KeyCode::Char((c as u8 - 0x1C + b'4') as char),
KeyModifiers::CONTROL,
)),
_ if (c as u32) <= 26 => Some(KeyEvent::new(
KeyCode::Char((b'a' - 1 + c as u8) as char),
KeyModifiers::CONTROL,
)),
'\u{1b}' => Some(KeyCode::Esc.into()),
c => Some(KeyCode::Char(c).into()),
},
Input::KeyDown => Some(KeyEvent {
code: KeyCode::Down,
modifiers: empty,
}),
Input::KeyUp => Some(KeyEvent {
code: KeyCode::Up,
modifiers: empty,
}),
Input::KeyLeft => Some(KeyEvent {
code: KeyCode::Left,
modifiers: empty,
}),
Input::KeyRight => Some(KeyEvent {
code: KeyCode::Right,
modifiers: empty,
}),
Input::KeyHome => Some(KeyEvent {
code: KeyCode::Home,
modifiers: empty,
}),
Input::KeyBackspace => Some(KeyEvent {
code: KeyCode::Backspace,
modifiers: empty,
}),
Input::KeyF0 => Some(KeyEvent {
code: KeyCode::F(0),
modifiers: empty,
}),
Input::KeyF1 => Some(KeyEvent {
code: KeyCode::F(1),
modifiers: empty,
}),
Input::KeyF2 => Some(KeyEvent {
code: KeyCode::F(2),
modifiers: empty,
}),
Input::KeyF3 => Some(KeyEvent {
code: KeyCode::F(3),
modifiers: empty,
}),
Input::KeyF4 => Some(KeyEvent {
code: KeyCode::F(4),
modifiers: empty,
}),
Input::KeyF5 => Some(KeyEvent {
code: KeyCode::F(5),
modifiers: empty,
}),
Input::KeyF6 => Some(KeyEvent {
code: KeyCode::F(6),
modifiers: empty,
}),
Input::KeyF7 => Some(KeyEvent {
code: KeyCode::F(7),
modifiers: empty,
}),
Input::KeyF8 => Some(KeyEvent {
code: KeyCode::F(8),
modifiers: empty,
}),
Input::KeyF9 => Some(KeyEvent {
code: KeyCode::F(9),
modifiers: empty,
}),
Input::KeyF10 => Some(KeyEvent {
code: KeyCode::F(10),
modifiers: empty,
}),
Input::KeyF11 => Some(KeyEvent {
code: KeyCode::F(11),
modifiers: empty,
}),
Input::KeyF12 => Some(KeyEvent {
code: KeyCode::F(12),
modifiers: empty,
}),
Input::KeyF13 => Some(KeyEvent {
code: KeyCode::F(13),
modifiers: empty,
}),
Input::KeyF14 => Some(KeyEvent {
code: KeyCode::F(14),
modifiers: empty,
}),
Input::KeyF15 => Some(KeyEvent {
code: KeyCode::F(15),
modifiers: empty,
}),
Input::KeyDL => Some(KeyEvent {
code: KeyCode::Delete,
modifiers: empty,
}),
Input::KeyIC => Some(KeyEvent {
code: KeyCode::Insert,
modifiers: empty,
}),
Input::KeyNPage => Some(KeyEvent {
code: KeyCode::PageDown,
modifiers: empty,
}),
Input::KeyPPage => Some(KeyEvent {
code: KeyCode::PageUp,
modifiers: empty,
}),
Input::KeyEnter => Some(KeyEvent {
code: KeyCode::Enter,
modifiers: empty,
}),
Input::KeyEnd => Some(KeyEvent {
code: KeyCode::End,
modifiers: empty,
}),
_ => None,
};
key_code.map(|e| e)
}
pub fn try_map_shift_key(&self, input: crosscurses::Input) -> Option<KeyEvent> {
let key_code = match input {
Input::KeySF => Some(KeyCode::Down),
Input::KeySR => Some(KeyCode::Up),
Input::KeySTab => Some(KeyCode::Tab),
Input::KeySDC => Some(KeyCode::Delete),
Input::KeySEnd => Some(KeyCode::End),
Input::KeySHome => Some(KeyCode::Home),
Input::KeySIC => Some(KeyCode::Insert),
Input::KeySLeft => Some(KeyCode::Left),
Input::KeySNext => Some(KeyCode::PageDown),
Input::KeySPrevious => Some(KeyCode::PageDown),
Input::KeySPrint => Some(KeyCode::End),
Input::KeySRight => Some(KeyCode::Right),
Input::KeyBTab => Some(KeyCode::BackTab),
_ => None,
};
key_code.map(|e| KeyEvent::new(e, KeyModifiers::SHIFT))
}
pub fn try_map_ctrl_key(&self, input: crosscurses::Input) -> Option<KeyEvent> {
let key_code = match input {
Input::KeyCTab => Some(KeyCode::Tab),
_ => None,
};
key_code.map(|e| KeyEvent::new(e, KeyModifiers::CONTROL))
}
pub fn try_map_ctrl_alt_key(&self, input: crosscurses::Input) -> Option<KeyEvent> {
let key_code = match input {
Input::KeyCATab => Some(KeyCode::Tab),
_ => None,
};
key_code.map(|e| KeyEvent::new(e, KeyModifiers::CONTROL | KeyModifiers::ALT))
}
pub fn try_map_non_key_event(&self, input: crosscurses::Input) -> Option<Event> {
match input {
Input::KeyResize => {
crosscurses::resize_term(0, 0);
Some(Event::Resize)
}
Input::KeyMouse => Some(self.map_mouse_event()),
Input::Unknown(code) => {
Some(
self.key_codes
.get(&(code + 256 + 48))
.cloned()
.unwrap_or_else(|| Event::Unknown),
)
}
_ => None,
}
}
fn map_mouse_event(&self) -> Event {
let mut mevent = match crosscurses::getmouse() {
Err(_) => return Event::Unknown,
Ok(event) => event,
};
let shift = (mevent.bstate & crosscurses::BUTTON_SHIFT as mmask_t) != 0;
let alt = (mevent.bstate & crosscurses::BUTTON_ALT as mmask_t) != 0;
let ctrl = (mevent.bstate & crosscurses::BUTTON_CTRL as mmask_t) != 0;
let mut modifiers = KeyModifiers::empty();
if shift {
modifiers |= KeyModifiers::SHIFT;
}
if ctrl {
modifiers |= KeyModifiers::CONTROL;
}
if alt {
modifiers |= KeyModifiers::ALT;
}
mevent.bstate &= !(crosscurses::BUTTON_SHIFT
| crosscurses::BUTTON_ALT
| crosscurses::BUTTON_CTRL) as mmask_t;
let (x, y) = (mevent.x as u16, mevent.y as u16);
if mevent.bstate == crosscurses::REPORT_MOUSE_POSITION as mmask_t {
self.last_btn()
.map(|btn| Event::Mouse(MouseEvent::Drag(btn, x, y, modifiers)))
.unwrap_or_else(|| {
Event::Unknown
})
} else {
let mut bare_event = mevent.bstate & ((1 << 25) - 1);
let mut event = None;
while bare_event != 0 {
let single_event = 1 << bare_event.trailing_zeros();
bare_event ^= single_event;
self.on_mouse_event(
single_event,
|e| {
if event.is_none() {
event = Some(e);
} else {
self.update_stored_event(Event::Mouse(e));
}
},
x,
y,
modifiers,
);
}
if let Some(event) = event {
if let Some(btn) = event.button() {
self.update_last_btn(btn);
}
Event::Mouse(event)
} else {
Event::Unknown
}
}
}
fn on_mouse_event<F>(
&self,
bare_event: mmask_t,
mut f: F,
x: u16,
y: u16,
modifiers: KeyModifiers,
) where
F: FnMut(MouseEvent),
{
let button = self.map_mouse_button(bare_event);
match bare_event {
crosscurses::BUTTON4_PRESSED => f(MouseEvent::ScrollUp(x, y, modifiers)),
crosscurses::BUTTON5_PRESSED => f(MouseEvent::ScrollDown(x, y, modifiers)),
crosscurses::BUTTON1_RELEASED
| crosscurses::BUTTON2_RELEASED
| crosscurses::BUTTON3_RELEASED
| crosscurses::BUTTON4_RELEASED
| crosscurses::BUTTON5_RELEASED => f(MouseEvent::Up(button, x, y, modifiers)),
crosscurses::BUTTON1_PRESSED
| crosscurses::BUTTON2_PRESSED
| crosscurses::BUTTON3_PRESSED => f(MouseEvent::Down(button, x, y, modifiers)),
crosscurses::BUTTON1_CLICKED
| crosscurses::BUTTON2_CLICKED
| crosscurses::BUTTON3_CLICKED
| crosscurses::BUTTON4_CLICKED
| crosscurses::BUTTON5_CLICKED => {
f(MouseEvent::Down(button, x, y, modifiers));
f(MouseEvent::Up(button, x, y, modifiers));
}
crosscurses::BUTTON1_DOUBLE_CLICKED
| crosscurses::BUTTON2_DOUBLE_CLICKED
| crosscurses::BUTTON3_DOUBLE_CLICKED
| crosscurses::BUTTON4_DOUBLE_CLICKED
| crosscurses::BUTTON5_DOUBLE_CLICKED => {
for _ in 0..2 {
f(MouseEvent::Down(button, x, y, modifiers));
f(MouseEvent::Up(button, x, y, modifiers));
}
}
crosscurses::BUTTON1_TRIPLE_CLICKED
| crosscurses::BUTTON2_TRIPLE_CLICKED
| crosscurses::BUTTON3_TRIPLE_CLICKED
| crosscurses::BUTTON4_TRIPLE_CLICKED
| crosscurses::BUTTON5_TRIPLE_CLICKED => {
for _ in 0..3 {
f(MouseEvent::Down(button, x, y, modifiers));
f(MouseEvent::Up(button, x, y, modifiers));
}
}
_ => { }
}
}
fn map_mouse_button(&self, bare_event: mmask_t) -> MouseButton {
match bare_event {
crosscurses::BUTTON1_RELEASED
| crosscurses::BUTTON1_PRESSED
| crosscurses::BUTTON1_CLICKED
| crosscurses::BUTTON1_DOUBLE_CLICKED
| crosscurses::BUTTON1_TRIPLE_CLICKED => MouseButton::Left,
crosscurses::BUTTON2_RELEASED
| crosscurses::BUTTON2_PRESSED
| crosscurses::BUTTON2_CLICKED
| crosscurses::BUTTON2_DOUBLE_CLICKED
| crosscurses::BUTTON2_TRIPLE_CLICKED => MouseButton::Middle,
crosscurses::BUTTON3_RELEASED
| crosscurses::BUTTON3_PRESSED
| crosscurses::BUTTON3_CLICKED
| crosscurses::BUTTON3_DOUBLE_CLICKED
| crosscurses::BUTTON3_TRIPLE_CLICKED => MouseButton::Right,
crosscurses::BUTTON4_RELEASED
| crosscurses::BUTTON4_PRESSED
| crosscurses::BUTTON4_CLICKED
| crosscurses::BUTTON4_DOUBLE_CLICKED
| crosscurses::BUTTON4_TRIPLE_CLICKED => MouseButton::Unknown,
crosscurses::BUTTON5_RELEASED
| crosscurses::BUTTON5_PRESSED
| crosscurses::BUTTON5_CLICKED
| crosscurses::BUTTON5_DOUBLE_CLICKED
| crosscurses::BUTTON5_TRIPLE_CLICKED => MouseButton::Unknown,
_ => MouseButton::Unknown,
}
}
}
pub fn find_closest(color: Color, max_colors: i16) -> i16 {
let color = if let Color::AnsiValue(val) = color {
Color::from(val)
} else {
color
};
match color {
Color::Black => crosscurses::COLOR_BLACK,
Color::DarkRed => crosscurses::COLOR_RED,
Color::DarkGreen => crosscurses::COLOR_GREEN,
Color::DarkYellow => crosscurses::COLOR_YELLOW,
Color::DarkBlue => crosscurses::COLOR_BLUE,
Color::DarkMagenta => crosscurses::COLOR_MAGENTA,
Color::DarkCyan => crosscurses::COLOR_CYAN,
Color::Grey => crosscurses::COLOR_WHITE,
Color::Red => 9 % max_colors,
Color::Green => 10 % max_colors,
Color::Yellow => 11 % max_colors,
Color::Blue => 12 % max_colors,
Color::Magenta => 13 % max_colors,
Color::Cyan => 14 % max_colors,
Color::White => 15 % max_colors,
Color::Rgb(r, g, b) if max_colors >= 256 => {
if r == g && g == b && r != 0 && r < 250 {
let n = (r - 8) / 10;
i16::from(232 + n)
} else {
let r = 6 * u16::from(r) / 256;
let g = 6 * u16::from(g) / 256;
let b = 6 * u16::from(b) / 256;
(16 + 36 * r + 6 * g + b) as i16
}
}
Color::Rgb(r, g, b) => {
let r = if r > 127 { 1 } else { 0 };
let g = if g > 127 { 1 } else { 0 };
let b = if b > 127 { 1 } else { 0 };
(r + 2 * g + 4 * b) as i16
}
_ => -1, }
}