use std;
use std::sync::{LazyLock, Mutex};
use {log, easycurses, pancurses, nsys};
use crate::{Application, Interface, Tree};
use crate::math::Vector2;
use crate::geometry::integer::Aabb2;
use crate::geometry::intersect::integer as intersect;
use crate::interface::{view, View};
use crate::widget;
use crate::tree::NodeId;
use super::{Graphics, Presentation};
pub static INIT_COLORS : LazyLock <Mutex <Option <[[u8; 3]; 8]>>> =
LazyLock::new (|| Mutex::new (None));
#[derive(Debug)]
pub struct Curses {
inner : nsys::curses::Curses,
release_buttons : Vec <view::input::Button>
}
pub fn border_acs_lines() -> view::Border {
#[expect(trivial_numeric_casts)]
view::Border {
top: pancurses::ACS_HLINE() as u32,
bottom: pancurses::ACS_HLINE() as u32,
left: pancurses::ACS_VLINE() as u32,
right: pancurses::ACS_VLINE() as u32,
top_left: pancurses::ACS_ULCORNER() as u32,
top_right: pancurses::ACS_URCORNER() as u32,
bottom_left: pancurses::ACS_LLCORNER() as u32,
bottom_right: pancurses::ACS_LRCORNER() as u32,
thickness_top: 1,
thickness_bottom: 1,
thickness_left: 1,
thickness_right: 1
}
}
impl Curses {
#[inline]
pub const fn inner (&self) -> &nsys::curses::Curses {
&self.inner
}
#[inline]
pub const fn inner_mut (&mut self) -> &mut nsys::curses::Curses {
&mut self.inner
}
#[inline]
pub fn dimensions (&self) -> view::dimensions::Tile {
let (rows, columns) = self.inner.dimensions_rc();
debug_assert!(rows > 0);
debug_assert!(columns > 0);
Vector2::new (rows as u32, columns as u32).into()
}
}
impl Default for Curses {
fn default() -> Self {
let mut inner = {
let init_colors = INIT_COLORS.lock().unwrap();
nsys::curses::Curses::new (init_colors.as_ref())
};
inner.un_get_input (pancurses::Input::KeyResize);
Curses { inner, release_buttons: Vec::new() }
}
}
impl Graphics for Curses { }
impl Presentation for Curses {
fn with_root (_root : View, _id : NodeId) -> Self {
Self::default()
}
fn make_interface <A : Application> () -> Interface <A, Self> {
use widget::BuildElement;
let screen = widget::frame::screen::TileBuilder::<A>::new().build_element();
let mut interface = Interface::<A, Self>::with_root (screen);
let dimensions = interface.presentation.dimensions();
let mut actions = vec![];
widget::frame::screen::resize (
&view::input::System::Resized {
width: dimensions.columns(),
height: dimensions.rows()
},
interface.elements(),
interface.root_id(),
&mut actions
);
let _ = interface.actions (actions);
interface
}
fn get_input (&mut self, input_buffer : &mut Vec <view::Input>) {
use view::{input, Input};
log::trace!("get input...");
input_buffer.extend (self.release_buttons.drain (..)
.map (|button|{
let input = Input::Button (button, input::button::State::Released);
log::debug!("input: {input:?}");
input
}));
if let Some (pancurses_input) = self.inner.getch_nowait() {
log::trace!("pancurses input: {pancurses_input:?}");
let input = match pancurses_input.into() {
Input::System (input::System::Resized { width: 0, height: 0 }) => {
let (width, height) = {
let (rows, columns) = self.inner.dimensions_rc();
debug_assert!(rows >= 0);
debug_assert!(columns >= 0);
(columns as u32, rows as u32)
};
input::System::Resized { width, height }.into()
}
Input::Button (button, pressed) => {
debug_assert_eq!(pressed, input::button::State::Pressed);
self.release_buttons.push (button);
Input::Button (button, pressed)
}
Input::Text (input::Text::Char (ch)) => {
let button = {
let (keycode, modifiers) = char_to_keycode (ch);
input::Button { variant: keycode.into(), modifiers }
};
let input = Input::Text (input::Text::Char (ch));
log::debug!("input: {input:?}");
input_buffer.push (input);
self.release_buttons.push (button);
(button, input::button::State::Pressed).into()
}
input => input
};
log::debug!("input: {input:?}");
input_buffer.push (input);
}
log::trace!("...get input");
}
fn display_view <V : AsRef <View>> (&mut self,
view_tree : &Tree <V>,
_display_values : std::vec::Drain <(NodeId, view::Display)>
) {
use std::collections::VecDeque;
use pancurses::chtype;
use nsys::curses::{color_pair_attr, pancurses_ok, pancurses_warn_ok};
use view::component::{canvas, Body, Canvas, Image, Kind};
log::trace!("display view...");
let (attrs, colors) = self.inner.win().attrget();
let bkgd = self.inner.win().getbkgd();
log::trace!(" window: attributes: {attrs:064b}");
log::trace!(" window: color pair: {colors}");
log::trace!(" window: background: {bkgd:064b}");
let screen_id = view_tree.root_node_id().unwrap();
let screen = view_tree.get (screen_id).unwrap().data().as_ref();
let screen_canvas = Canvas::try_ref (&screen.component).unwrap();
let (clear, bg_color) = {
let color = match screen_canvas.clear_color {
canvas::ClearColor::Appearance =>
screen.appearance.style.as_ref().map(|style| style.bg),
canvas::ClearColor::Fixed (clear_color) => clear_color
};
( color.is_some(),
color.map_or (0x00, |color| {
let bg = convert_color (color).unwrap();
color_pair_attr (easycurses::Color::White, bg)
})
)
};
self.inner.win().bkgdset (' ' as chtype | bg_color);
if clear {
pancurses_ok!(self.inner.win().erase());
}
let style = screen.appearance.style.clone().unwrap_or_default();
#[expect(trivial_numeric_casts)]
if let Some (border) = screen_canvas.border.as_ref() {
let rc_min = (0,0);
let rc_max = (
screen_canvas.coordinates.dimensions().vec().numcast().unwrap()
- Vector2::new (1,1)
).into_tuple();
let color = color_pair_attr (
convert_color (style.fg).unwrap(),
convert_color (style.bg).unwrap());
pancurses_warn_ok!(self.inner.draw_border (
border.left as chtype | color,
border.right as chtype | color,
border.top as chtype | color,
border.bottom as chtype | color,
border.top_left as chtype | color,
border.top_right as chtype | color,
border.bottom_left as chtype | color,
border.bottom_right as chtype | color,
rc_min, rc_max,
border.thickness_top as u32,
border.thickness_bottom as u32,
border.thickness_left as u32,
border.thickness_right as u32
));
}
let screen_aabb = screen_canvas.coordinates.into();
let mut canvases = VecDeque::new();
for child_id in view_tree.children_ids (screen_id).unwrap() {
let child = view_tree.get (child_id).unwrap().data().as_ref();
let style = child.appearance.style.as_ref().unwrap_or (&style);
if let Some (child_canvas) = Canvas::try_ref (&child.component) &&
let Some ((canvas, body_aabb)) =
canvas_and_body_aabb (child_canvas, screen_aabb)
{
canvases.push_back ((child_id, canvas, style, body_aabb));
}
}
while canvases.len() > 0 {
let (node_id, canvas, style, body_aabb) = canvases.pop_front().unwrap();
let node = view_tree.get (node_id).unwrap();
let mut text = None;
let mut image = None;
for child_id in node.children().iter().rev() {
let child = view_tree.get (child_id).unwrap().data().as_ref();
let style = child.appearance.style.as_ref().unwrap_or (style);
if let Some (child_canvas) = Canvas::try_ref (&child.component) &&
let Some ((canvas, body_aabb)) =
canvas_and_body_aabb (child_canvas, screen_aabb)
{
canvases.push_front ((child_id, canvas, style, body_aabb));
} else if let Some (child_text) = Body::try_ref (&child.component) {
debug_assert!(text.is_none());
let skip = body_aabb.min().0.x as usize;
let take = body_aabb.max().0.x as usize - skip;
let start = body_aabb.min().0.y as usize;
let end = body_aabb.max().0.y as usize;
text = Some ((
child_text.0.lines().skip (skip).take (take).map (
move |line| &line[start.min (line.len())..end.min (line.len())]),
child.appearance.style.clone()
));
} else if let Some (child_image) = Image::try_ref (&child.component) {
debug_assert!(image.is_none());
match child_image {
Image::Raw64 (pixmap) => {
let skip = body_aabb.min().0.x as usize;
let take = body_aabb.max().0.x as usize - skip;
let start = body_aabb.min().0.y as usize;
let end = body_aabb.max().0.y as usize;
image = Some (pixmap.rows().skip (skip).take (take).map (
move |row| &row[start.min (row.len())..end.min (row.len())]
));
}
Image::Raw8(_) | Image::Raw16(_) | Image::Raw32(_) |
Image::Texture(_) => {
image = None;
}
}
}
}
self.draw_canvas (&canvas, style, text, image, true);
}
log::trace!("...display view");
fn canvas_and_body_aabb (child_canvas : &Canvas, screen_aabb : Aabb2 <i32>)
-> Option <(Canvas, Aabb2 <i32>)>
{
let child_aabb = child_canvas.coordinates.into();
intersect::continuous_aabb2_aabb2 (screen_aabb, child_aabb).map (
|intersection| {
let coordinates = view::Coordinates::tile_from_aabb (intersection);
let mut canvas = Canvas {
coordinates, .. child_canvas.clone()
};
let (body_width, body_height) = child_canvas.body_wh();
let (mut body_min_row, mut body_min_col) = (0, 0);
let (mut body_max_row, mut body_max_col) =
(body_height as i32, body_width as i32);
if let Some (border) = canvas.border.as_mut() {
let outside_top = screen_aabb.min().0.x - child_aabb.min().0.x;
if outside_top > 0 {
body_min_row = (outside_top as u32)
.saturating_sub (border.thickness_top as u32) as i32;
border.thickness_top = border.thickness_top
.saturating_sub (outside_top as u16);
}
let outside_bottom = child_aabb.max().0.x - screen_aabb.max().0.x;
if outside_bottom > 0 {
body_max_row -= (outside_bottom as u32)
.saturating_sub (border.thickness_bottom as u32) as i32;
border.thickness_bottom = border.thickness_bottom
.saturating_sub (outside_bottom as u16);
}
let outside_left = screen_aabb.min().0.y - child_aabb.min().0.y;
if outside_left > 0 {
body_min_col = (outside_left as u32)
.saturating_sub (border.thickness_left as u32) as i32;
border.thickness_left = border.thickness_left
.saturating_sub (outside_left as u16);
}
let outside_right = child_aabb.max().0.y - screen_aabb.max().0.y;
if outside_right > 0 {
body_max_col -= (outside_right as u32)
.saturating_sub (border.thickness_right as u32) as i32;
border.thickness_right = border.thickness_right
.saturating_sub (outside_right as u16);
}
}
( canvas,
Aabb2::with_minmax (
[body_min_row, body_min_col].into(),
[body_max_row, body_max_col].into()
)
)
}
)
}
}
}
impl Curses {
fn draw_canvas <'a> (&mut self,
canvas : &view::component::Canvas,
style : &view::Style,
text : Option <(impl Iterator <Item=&'a str>, Option <view::Style>)>,
image : Option <impl Iterator <Item=&'a [u64]>>,
_debug : bool
) {
use std::convert::TryInto;
use nsys::curses::{chtype_color_pair, color_pair_attr, pancurses_ok,
pancurses_warn_ok};
use pancurses::chtype;
use crate::interface::view::component::canvas;
log::trace!("draw_canvas...");
let curses = &mut self.inner;
let (position, dimensions)
: (view::position::Tile, view::dimensions::Tile)
= canvas.coordinates.try_into().unwrap();
let (rc_min, rc_max) = {
let (row, col) = (position.row(), position.column());
let (rows, cols) = (dimensions.rows() as i32, dimensions.columns() as i32);
( (row, col),
(row + rows-1, col + cols-1) )
};
let clear_color = match canvas.clear_color {
canvas::ClearColor::Appearance => Some (style.bg),
canvas::ClearColor::Fixed (color) => color
};
clear_color.map (|color|{
let border = ' ' as chtype;
let bgcolor = convert_color (color).unwrap();
let fill_color = color_pair_attr (easycurses::Color::White, bgcolor);
let fill = ' ' as chtype | fill_color;
curses.draw_rect (border, fill, rc_min, rc_max, 0,0); });
let color = color_pair_attr (
convert_color (style.fg).unwrap(),
convert_color (style.bg).unwrap());
let color_pair = chtype_color_pair (color);
pancurses_ok!(curses.win().color_set (color_pair));
curses.win().bkgdset (b' ' as chtype | color);
#[expect(trivial_numeric_casts)]
let (border_thickness_top, border_thickness_left) =
canvas.border.as_ref().map (|border|{
pancurses_warn_ok!(curses.draw_border (
border.left as chtype | color,
border.right as chtype | color,
border.top as chtype | color,
border.bottom as chtype | color,
border.top_left as chtype | color,
border.top_right as chtype | color,
border.bottom_left as chtype | color,
border.bottom_right as chtype | color,
rc_min, rc_max,
border.thickness_top as u32,
border.thickness_bottom as u32,
border.thickness_left as u32,
border.thickness_right as u32
));
( border.thickness_top as i32,
border.thickness_left as i32 )
}).unwrap_or_default();
text.map (|(text, style)|{
style.map (|style|{
let color = color_pair_attr (
convert_color (style.fg).unwrap(),
convert_color (style.bg).unwrap());
let color_pair = chtype_color_pair (color);
pancurses_ok!(curses.win().color_set (color_pair));
curses.win().bkgdset (b' ' as chtype | color);
});
for (i, line) in text.enumerate() {
let printable = line.trim_start_matches ('\0');
let skip = line.len() - printable.len();
let row = rc_min.0 + border_thickness_top + i as i32;
let col = rc_min.1 + border_thickness_left + skip as i32;
if line.len() > 0 {
let _ = curses.win().mvaddstr (row, col, printable);
}
}
});
image.map (|image|{
for (i, line) in image.enumerate() {
let row = rc_min.0 + border_thickness_top + i as i32;
let col = rc_min.1 + border_thickness_left;
for (j, ch) in line.iter().enumerate() {
if *ch != 0x0 {
let _ = curses.win().mvaddch (row, col+j as i32, *ch as chtype);
}
}
}
});
log::trace!("...draw_canvas");
}
}
impl From <pancurses::Input> for view::Input {
fn from (input : pancurses::Input) -> Self {
use pancurses::Input::*;
use view::input::{self, button::Keycode, Modifiers};
const ALT : Modifiers = Modifiers::ALT;
const CTRL : Modifiers = Modifiers::CTRL;
const SHIFT : Modifiers = Modifiers::SHIFT;
const EMPTY : Modifiers = Modifiers::empty();
let unhandled = |input| {
log::warn!("curses unhandled input: {input:?}");
(Keycode::NonConvert, EMPTY)
};
#[expect(clippy::match_same_arms)]
let (keycode, modifiers) = match input {
Character (ch) => match ch {
'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' |
'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' |
'y' | 'z' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0' |
'-' | '=' | '`' | '[' | ']' | '\\'| ';' | '\''| ',' | '.' | '/' | 'A' |
'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' |
'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' |
'Z' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')' | '_' |
'+' | '~' | '{' | '}' | '|' | ':' | '"' | '<' | '>' | '?' | ' ' | '\n'|
'\t' => return input::Text::Char (ch).into(),
special => {
let unhandled = |ch| {
log::warn!("curses unhandled special character: {ch:?}");
(Keycode::NonConvert, EMPTY)
};
let keyname = pancurses::keyname (special as i32).unwrap_or_else (||{
log::error!("pancurses keyname failed for special char {special:?}");
unreachable!()
});
log::trace!("keyname: {keyname:?}");
match keyname.as_str() {
"^A" => (Keycode::A, CTRL),
"^B" => (Keycode::B, CTRL),
"^C" => (Keycode::C, CTRL),
"^D" => (Keycode::D, CTRL),
"^E" => (Keycode::E, CTRL),
"^F" => (Keycode::F, CTRL),
"^G" => (Keycode::G, CTRL),
"^H" => (Keycode::Backspace, EMPTY),
"^I" => (Keycode::I, CTRL),
"^J" => (Keycode::J, CTRL),
"^K" => (Keycode::K, CTRL),
"^L" => (Keycode::L, CTRL),
"^M" => (Keycode::M, CTRL),
"^N" => (Keycode::N, CTRL),
"^O" => (Keycode::O, CTRL),
"^P" => (Keycode::P, CTRL),
"^Q" => (Keycode::Q, CTRL),
"^R" => (Keycode::R, CTRL),
"^S" => (Keycode::S, CTRL),
"^T" => (Keycode::T, CTRL),
"^U" => (Keycode::U, CTRL),
"^V" => (Keycode::V, CTRL),
"^W" => (Keycode::W, CTRL),
"^X" => (Keycode::X, CTRL),
"^Y" => (Keycode::Y, CTRL),
"^Z" => (Keycode::Z, CTRL),
"^_" => (Keycode::Minus, SHIFT | CTRL),
"^?" => (Keycode::Backspace, EMPTY),
"^[" => (Keycode::Escape, EMPTY), "^@" => (Keycode::Key2, CTRL),
"KEY_PAUSE" => (Keycode::Pause, EMPTY),
"KEY_SCROLLLOCK" => (Keycode::ScrollLock, EMPTY),
"KEY_SDOWN" => (Keycode::Down, SHIFT),
"KEY_SUP" => (Keycode::Up, SHIFT),
"KEY_B2" => (Keycode::Numpad5, SHIFT),
"SHF_PADENTER" => (Keycode::NumpadEnter, SHIFT),
"SHF_PADMINUS" => (Keycode::NumpadSubtract, SHIFT),
"SHF_PADPLUS" => (Keycode::NumpadAdd, SHIFT),
"SHF_PADSLASH" => (Keycode::NumpadDivide, SHIFT),
"SHF_PADSTAR" => (Keycode::NumpadMultiply, SHIFT),
"KEY_F(25)" => (Keycode::F1, CTRL),
"KEY_F(26)" => (Keycode::F2, CTRL),
"KEY_F(27)" => (Keycode::F3, CTRL),
"KEY_F(28)" => (Keycode::F4, CTRL),
"KEY_F(29)" => (Keycode::F5, CTRL),
"KEY_F(30)" => (Keycode::F6, CTRL),
"KEY_F(31)" => (Keycode::F7, CTRL),
"KEY_F(32)" => (Keycode::F8, CTRL),
"KEY_F(33)" => (Keycode::F9, CTRL),
"KEY_F(34)" => (Keycode::F10, CTRL),
"KEY_F(35)" => (Keycode::F11, CTRL),
"KEY_F(36)" => (Keycode::F12, CTRL),
"KEY_F(37)" => (Keycode::F1, ALT),
"KEY_F(38)" => (Keycode::F2, ALT),
"KEY_F(39)" => (Keycode::F3, ALT),
"KEY_F(40)" => (Keycode::F4, ALT),
"KEY_F(41)" => (Keycode::F5, ALT),
"KEY_F(42)" => (Keycode::F6, ALT),
"KEY_F(43)" => (Keycode::F7, ALT),
"KEY_F(44)" => (Keycode::F8, ALT),
"KEY_F(45)" => (Keycode::F9, ALT),
"KEY_F(46)" => (Keycode::F10, ALT),
"KEY_F(47)" => (Keycode::F11, ALT),
"KEY_F(48)" => (Keycode::F12, ALT),
"CTL_0" => (Keycode::Key0, CTRL),
"CTL_1" => (Keycode::Key1, CTRL),
"CTL_2" => (Keycode::Key2, CTRL),
"CTL_3" => (Keycode::Key3, CTRL),
"CTL_4" => (Keycode::Key4, CTRL),
"CTL_5" => (Keycode::Key5, CTRL),
"CTL_6" => (Keycode::Key6, CTRL),
"CTL_7" => (Keycode::Key7, CTRL),
"CTL_8" => (Keycode::Key8, CTRL),
"CTL_9" => (Keycode::Key9, CTRL),
"CTL_DOWN" => (Keycode::Down, CTRL),
"CTL_LEFT" => (Keycode::Left, CTRL),
"CTL_RIGHT" => (Keycode::Right, CTRL),
"CTL_UP" => (Keycode::Up, CTRL),
"CTL_PAD0" => (Keycode::Numpad0, CTRL),
"CTL_PAD1" => (Keycode::Numpad1, CTRL),
"CTL_PAD2" => (Keycode::Numpad2, CTRL),
"CTL_PAD3" => (Keycode::Numpad3, CTRL),
"CTL_PAD4" => (Keycode::Numpad4, CTRL),
"CTL_PAD5" => (Keycode::Numpad5, CTRL),
"CTL_PAD6" => (Keycode::Numpad6, CTRL),
"CTL_PAD7" => (Keycode::Numpad7, CTRL),
"CTL_PAD8" => (Keycode::Numpad8, CTRL),
"CTL_PAD9" => (Keycode::Numpad9, CTRL),
"CTL_PADENTER" => (Keycode::NumpadEnter, CTRL),
"CTL_PADMINUS" => (Keycode::NumpadSubtract, CTRL),
"CTL_PADPLUS" => (Keycode::NumpadAdd, CTRL),
"CTL_PADSLASH" => (Keycode::NumpadDivide, CTRL),
"CTL_PADSTOP" => (Keycode::NumpadDecimal, CTRL),
"CTL_PADSTAR" => (Keycode::NumpadMultiply, CTRL),
"CTL_BQUOTE" => (Keycode::Grave, CTRL),
"CTL_DEL" => (Keycode::Delete, CTRL),
"CTL_END" => (Keycode::End, CTRL),
"CTL_FLASH" => (Keycode::Slash, CTRL),
"CTL_HOME" => (Keycode::Home, CTRL),
"CTL_INS" => (Keycode::Insert, CTRL),
"CTL_PAUSE" => (Keycode::Pause, CTRL),
"CTL_PGDN" => (Keycode::PageDown, CTRL),
"CTL_PGUP" => (Keycode::PageUp, CTRL),
"CTL_SEMICOLON" => (Keycode::Semicolon, CTRL),
"CTL_STOP" => (Keycode::Period, CTRL),
"CTL_TAB" => (Keycode::Tab, CTRL),
"ALT_0" => (Keycode::Key0, ALT),
"ALT_1" => (Keycode::Key1, ALT),
"ALT_2" => (Keycode::Key2, ALT),
"ALT_3" => (Keycode::Key3, ALT),
"ALT_4" => (Keycode::Key4, ALT),
"ALT_5" => (Keycode::Key5, ALT),
"ALT_6" => (Keycode::Key6, ALT),
"ALT_7" => (Keycode::Key7, ALT),
"ALT_8" => (Keycode::Key8, ALT),
"ALT_9" => (Keycode::Key9, ALT),
"ALT_A" => (Keycode::A, ALT),
"ALT_B" => (Keycode::B, ALT),
"ALT_C" => (Keycode::C, ALT),
"ALT_D" => (Keycode::D, ALT),
"ALT_E" => (Keycode::E, ALT),
"ALT_F" => (Keycode::F, ALT),
"ALT_G" => (Keycode::G, ALT),
"ALT_H" => (Keycode::H, ALT),
"ALT_I" => (Keycode::I, ALT),
"ALT_J" => (Keycode::J, ALT),
"ALT_K" => (Keycode::K, ALT),
"ALT_L" => (Keycode::L, ALT),
"ALT_M" => (Keycode::M, ALT),
"ALT_N" => (Keycode::N, ALT),
"ALT_O" => (Keycode::O, ALT),
"ALT_P" => (Keycode::P, ALT),
"ALT_Q" => (Keycode::Q, ALT),
"ALT_R" => (Keycode::R, ALT),
"ALT_S" => (Keycode::S, ALT),
"ALT_T" => (Keycode::T, ALT),
"ALT_U" => (Keycode::U, ALT),
"ALT_V" => (Keycode::V, ALT),
"ALT_W" => (Keycode::W, ALT),
"ALT_X" => (Keycode::X, ALT),
"ALT_Y" => (Keycode::Y, ALT),
"ALT_Z" => (Keycode::Z, ALT),
"ALT_BKSP" => (Keycode::Backspace, ALT),
"ALT_BQUOTE" => (Keycode::Grave, ALT),
"ALT_BSLASH" => (Keycode::Backslash, ALT),
"^\\" => (Keycode::LBracket, ALT),
"ALT_COMMA" => (Keycode::Comma, ALT),
"ALT_DEL" => (Keycode::Delete, ALT),
"ALT_END" => (Keycode::End, ALT),
"ALT_ENTER" => (Keycode::Enter, ALT),
"ALT_EQUALS" => (Keycode::Equal, ALT),
"ALT_HOME" => (Keycode::Home, ALT),
"ALT_INS" => (Keycode::Insert, ALT),
"ALT_FQUOTE" => (Keycode::Quote, ALT),
"ALT_FSLASH" => (Keycode::Slash, ALT),
"ALT_MINUS" => (Keycode::Minus, ALT),
"ALT_PGDN" => (Keycode::PageDown, ALT),
"ALT_PGUP" => (Keycode::PageUp, ALT),
"ALT_RBRACKET" => (Keycode::RBracket, ALT),
"ALT_SCROLLLOCK" => (Keycode::ScrollLock, ALT),
"ALT_SEMICOLON" => (Keycode::Semicolon, ALT),
"ALT_STOP" => (Keycode::Period, ALT),
"ALT_PADENTER" => (Keycode::NumpadEnter, ALT),
"ALT_PADMINUS" => (Keycode::NumpadSubtract, ALT),
"ALT_PADSLASH" => (Keycode::NumpadDivide, ALT),
"ALT_PADSTAR" => (Keycode::NumpadMultiply, ALT),
"ALT_PADSTOP" => (Keycode::NumpadDecimal, ALT),
ch => unhandled (ch)
}
}
},
Unknown(_) => unhandled (input),
KeyCodeYes => unhandled (input),
KeyBreak => (Keycode::Pause, EMPTY),
KeyDown => (Keycode::Down, EMPTY),
KeyUp => (Keycode::Up, EMPTY),
KeyLeft => (Keycode::Left, EMPTY),
KeyRight => (Keycode::Right, EMPTY),
KeyHome => (Keycode::Home, EMPTY),
KeyBackspace => (Keycode::Backspace, EMPTY),
KeyF0 => unhandled (input),
KeyF1 => (Keycode::F1, EMPTY),
KeyF2 => (Keycode::F2, EMPTY),
KeyF3 => (Keycode::F3, EMPTY),
KeyF4 => (Keycode::F4, EMPTY),
KeyF5 => (Keycode::F5, EMPTY),
KeyF6 => (Keycode::F6, EMPTY),
KeyF7 => (Keycode::F7, EMPTY),
KeyF8 => (Keycode::F8, EMPTY),
KeyF9 => (Keycode::F9, EMPTY),
KeyF10 => (Keycode::F10, EMPTY),
KeyF11 => (Keycode::F11, EMPTY),
KeyF12 => (Keycode::F12, EMPTY),
KeyF13 => (Keycode::F13, EMPTY),
KeyF14 => (Keycode::F14, EMPTY),
KeyF15 => (Keycode::F15, EMPTY),
KeyDL => unhandled (input),
KeyIL => unhandled (input),
KeyDC => (Keycode::Delete, EMPTY),
KeyIC => (Keycode::Insert, EMPTY),
KeyEIC => unhandled (input),
KeyClear => unhandled (input),
KeyEOS => unhandled (input),
KeyEOL => unhandled (input),
KeySF => (Keycode::Down, SHIFT),
KeySR => (Keycode::Up, SHIFT),
KeyNPage => (Keycode::PageDown, EMPTY),
KeyPPage => (Keycode::PageUp, EMPTY),
KeySTab => unhandled (input), KeyCTab => unhandled (input),
KeyCATab => unhandled (input),
KeyEnter => (Keycode::Enter, EMPTY),
KeySReset => unhandled (input),
KeyReset => unhandled (input),
KeyPrint => unhandled (input),
KeyLL => unhandled (input),
KeyAbort => unhandled (input),
KeySHelp => unhandled (input),
KeyLHelp => unhandled (input),
KeyBTab => (Keycode::Tab, SHIFT),
KeyBeg => unhandled (input),
KeyCancel => unhandled (input),
KeyClose => unhandled (input),
KeyCommand => unhandled (input),
KeyCopy => (Keycode::Copy, EMPTY),
KeyCreate => unhandled (input),
KeyEnd => (Keycode::End, EMPTY),
KeyExit => unhandled (input),
KeyFind => unhandled (input),
KeyHelp => unhandled (input),
KeyMark => unhandled (input),
KeyMessage => unhandled (input),
KeyMove => unhandled (input),
KeyNext => unhandled (input),
KeyOpen => unhandled (input),
KeyOptions => unhandled (input),
KeyPrevious => unhandled (input),
KeyRedo => unhandled (input),
KeyReference => unhandled (input),
KeyRefresh => unhandled (input),
KeyReplace => unhandled (input),
KeyRestart => unhandled (input),
KeyResume => unhandled (input),
KeySave => unhandled (input),
KeySBeg => unhandled (input),
KeySCancel => unhandled (input),
KeySCommand => unhandled (input),
KeySCopy => unhandled (input),
KeySCreate => unhandled (input),
KeySDC => (Keycode::Delete, SHIFT),
KeySDL => unhandled (input),
KeySelect => unhandled (input),
KeySEnd => (Keycode::End, SHIFT),
KeySEOL => unhandled (input),
KeySExit => unhandled (input),
KeySFind => unhandled (input),
KeySHome => (Keycode::Home, SHIFT),
KeySIC => (Keycode::Insert, SHIFT),
KeySLeft | KeySMessage => (Keycode::Left, SHIFT),
KeySMove => unhandled (input),
KeySNext => (Keycode::PageDown, SHIFT),
KeySOptions => unhandled (input),
KeySPrevious => (Keycode::PageUp, SHIFT),
KeySPrint => unhandled (input),
KeySRedo => unhandled (input),
KeySReplace => unhandled (input),
KeySRight | KeySResume => (Keycode::Right, SHIFT),
KeySSave => unhandled (input),
KeySSuspend => unhandled (input),
KeySUndo => unhandled (input),
KeySuspend => unhandled (input),
KeyUndo => unhandled (input),
KeyResize => return input::System::Resized { width: 0, height: 0 }.into(),
KeyEvent => unhandled (input),
KeyMouse => unhandled (input),
KeyA1 => unhandled (input),
KeyA3 => unhandled (input),
KeyB2 => unhandled (input),
KeyC1 => unhandled (input),
KeyC3 => unhandled (input)
};
( input::Button {
variant: keycode.into(),
modifiers
},
input::button::State::Pressed
).into()
}
}
fn char_to_keycode (ch : char)
-> (view::input::button::Keycode, view::input::Modifiers)
{
use view::input::Modifiers;
use view::input::button::Keycode;
const EMPTY : Modifiers = Modifiers::empty();
const SHIFT : Modifiers = Modifiers::SHIFT;
match ch {
'a' => (Keycode::A, EMPTY),
'b' => (Keycode::B, EMPTY),
'c' => (Keycode::C, EMPTY),
'd' => (Keycode::D, EMPTY),
'e' => (Keycode::E, EMPTY),
'f' => (Keycode::F, EMPTY),
'g' => (Keycode::G, EMPTY),
'h' => (Keycode::H, EMPTY),
'i' => (Keycode::I, EMPTY),
'j' => (Keycode::J, EMPTY),
'k' => (Keycode::K, EMPTY),
'l' => (Keycode::L, EMPTY),
'm' => (Keycode::M, EMPTY),
'n' => (Keycode::N, EMPTY),
'o' => (Keycode::O, EMPTY),
'p' => (Keycode::P, EMPTY),
'q' => (Keycode::Q, EMPTY),
'r' => (Keycode::R, EMPTY),
's' => (Keycode::S, EMPTY),
't' => (Keycode::T, EMPTY),
'u' => (Keycode::U, EMPTY),
'v' => (Keycode::V, EMPTY),
'w' => (Keycode::W, EMPTY),
'x' => (Keycode::X, EMPTY),
'y' => (Keycode::Y, EMPTY),
'z' => (Keycode::Z, EMPTY),
'1' => (Keycode::Key1, EMPTY),
'2' => (Keycode::Key2, EMPTY),
'3' => (Keycode::Key3, EMPTY),
'4' => (Keycode::Key4, EMPTY),
'5' => (Keycode::Key5, EMPTY),
'6' => (Keycode::Key6, EMPTY),
'7' => (Keycode::Key7, EMPTY),
'8' => (Keycode::Key8, EMPTY),
'9' => (Keycode::Key9, EMPTY),
'0' => (Keycode::Key0, EMPTY),
'-' => (Keycode::Minus, EMPTY),
'=' => (Keycode::Equal, EMPTY),
'`' => (Keycode::Grave, EMPTY),
'[' => (Keycode::LBracket, EMPTY),
']' => (Keycode::RBracket, EMPTY),
'\\' => (Keycode::Backslash, EMPTY),
';' => (Keycode::Semicolon, EMPTY),
'\'' => (Keycode::Quote, EMPTY),
',' => (Keycode::Comma, EMPTY),
'.' => (Keycode::Period, EMPTY),
'/' => (Keycode::Slash, EMPTY),
'A' => (Keycode::A, SHIFT),
'B' => (Keycode::B, SHIFT),
'C' => (Keycode::C, SHIFT),
'D' => (Keycode::D, SHIFT),
'E' => (Keycode::E, SHIFT),
'F' => (Keycode::F, SHIFT),
'G' => (Keycode::G, SHIFT),
'H' => (Keycode::H, SHIFT),
'I' => (Keycode::I, SHIFT),
'J' => (Keycode::J, SHIFT),
'K' => (Keycode::K, SHIFT),
'L' => (Keycode::L, SHIFT),
'M' => (Keycode::M, SHIFT),
'N' => (Keycode::N, SHIFT),
'O' => (Keycode::O, SHIFT),
'P' => (Keycode::P, SHIFT),
'Q' => (Keycode::Q, SHIFT),
'R' => (Keycode::R, SHIFT),
'S' => (Keycode::S, SHIFT),
'T' => (Keycode::T, SHIFT),
'U' => (Keycode::U, SHIFT),
'V' => (Keycode::V, SHIFT),
'W' => (Keycode::W, SHIFT),
'X' => (Keycode::X, SHIFT),
'Y' => (Keycode::Y, SHIFT),
'Z' => (Keycode::Z, SHIFT),
'!' => (Keycode::Key1, SHIFT),
'@' => (Keycode::Key2, SHIFT),
'#' => (Keycode::Key3, SHIFT),
'$' => (Keycode::Key4, SHIFT),
'%' => (Keycode::Key5, SHIFT),
'^' => (Keycode::Key6, SHIFT),
'&' => (Keycode::Key7, SHIFT),
'*' => (Keycode::Key8, SHIFT),
'(' => (Keycode::Key9, SHIFT),
')' => (Keycode::Key0, SHIFT),
'_' => (Keycode::Minus, SHIFT),
'+' => (Keycode::Equal, SHIFT),
'~' => (Keycode::Grave, SHIFT),
'{' => (Keycode::LBracket, SHIFT),
'}' => (Keycode::RBracket, SHIFT),
'|' => (Keycode::Backslash, SHIFT),
':' => (Keycode::Semicolon, SHIFT),
'"' => (Keycode::Quote, SHIFT),
'<' => (Keycode::Comma, SHIFT),
'>' => (Keycode::Period, SHIFT),
'?' => (Keycode::Slash, SHIFT),
' ' => (Keycode::Space, EMPTY),
'\n' => (Keycode::Enter, EMPTY),
'\t' => (Keycode::Tab, EMPTY),
_ => unreachable!()
}
}
const fn convert_color (color : view::Color)
-> Result <easycurses::Color, view::Color>
{
use view::color::*;
let color = match color {
Color::Named (named) => match named {
Named::Monochrome (Monochrome::White) => easycurses::Color::White,
Named::Monochrome (Monochrome::Grey) => return Err (color),
Named::Monochrome (Monochrome::Black) => easycurses::Color::Black,
Named::Hue (hue) => match hue {
Hue::Primary (Primary::Red) => easycurses::Color::Red,
Hue::Primary (Primary::Green) => easycurses::Color::Green,
Hue::Primary (Primary::Blue) => easycurses::Color::Blue,
Hue::Secondary (Secondary::Cyan) => easycurses::Color::Cyan,
Hue::Secondary (Secondary::Yellow) => easycurses::Color::Yellow,
Hue::Secondary (Secondary::Magenta) => easycurses::Color::Magenta,
_ => return Err (color)
}
},
Color::Raw (_) => return Err (color)
};
Ok (color)
}