gooey/presentation/
curses.rs

1//! Curses (terminal) backend
2
3use std;
4use std::sync;
5use {log, easycurses, pancurses, nsys};
6use lazy_static::lazy_static;
7
8use crate::{Application, Interface, Tree};
9use crate::math::Vector2;
10use crate::geometry::integer::Aabb2;
11use crate::geometry::intersect::integer as intersect;
12use crate::interface::{view, View};
13use crate::widget;
14use crate::tree::NodeId;
15use super::{Graphics, Presentation};
16
17lazy_static!{
18  pub static ref INIT_COLORS : sync::Mutex <Option <[[u8; 3]; 8]>> =
19    sync::Mutex::new (None);
20}
21
22/// A pancurses-based presentation implementation.
23#[derive(Debug)]
24pub struct Curses {
25  inner           : nsys::curses::Curses,
26  /// Pancuses doesn't generate release button events, so we simulate them by
27  /// generating a release event on the next frame for each press. Note that
28  /// this includes key repeat events.
29  release_buttons : Vec <view::input::Button>
30}
31
32pub fn border_acs_lines() -> view::Border {
33  view::Border {
34    top:              pancurses::ACS_HLINE()    as u32,
35    bottom:           pancurses::ACS_HLINE()    as u32,
36    left:             pancurses::ACS_VLINE()    as u32,
37    right:            pancurses::ACS_VLINE()    as u32,
38    top_left:         pancurses::ACS_ULCORNER() as u32,
39    top_right:        pancurses::ACS_URCORNER() as u32,
40    bottom_left:      pancurses::ACS_LLCORNER() as u32,
41    bottom_right:     pancurses::ACS_LRCORNER() as u32,
42    thickness_top:    1,
43    thickness_bottom: 1,
44    thickness_left:   1,
45    thickness_right:  1
46  }
47}
48
49impl Curses {
50  #[inline]
51  pub fn inner (&self) -> &nsys::curses::Curses {
52    &self.inner
53  }
54  #[inline]
55  pub fn inner_mut (&mut self) -> &mut nsys::curses::Curses {
56    &mut self.inner
57  }
58  #[inline]
59  pub fn dimensions (&self) -> view::dimensions::Tile {
60    let (rows, columns) = self.inner.dimensions_rc();
61    debug_assert!(rows > 0);
62    debug_assert!(columns > 0);
63    Vector2::new (rows as u32, columns as u32).into()
64  }
65}
66
67impl Default for Curses {
68  fn default() -> Self {
69    let init_colors = INIT_COLORS.lock().unwrap();
70    let mut inner = nsys::curses::Curses::new (init_colors.as_ref());
71    // push a resize event so the interface screen element can be initialized
72    inner.un_get_input (pancurses::Input::KeyResize);
73    Curses { inner, release_buttons: Vec::new() }
74  }
75}
76
77impl Graphics     for Curses { }
78impl Presentation for Curses {
79  fn with_root (_root : View, _id : NodeId) -> Self {
80    Self::default()
81  }
82
83  /// Create a new interface with a root screen frame element
84  fn make_interface <A : Application> () -> Interface <A, Self> {
85    use widget::BuildElement;
86    // create a root screen frame and use the resize control function to
87    // update the dimensions
88    let screen = widget::frame::screen::TileBuilder::<A>::new().build_element();
89    let mut interface = Interface::<A, Self>::with_root (screen);
90    let dimensions = interface.presentation.dimensions();
91    let mut actions = vec![];
92    widget::frame::screen::resize (
93      &view::input::System::Resized {
94        width:  dimensions.columns(),
95        height: dimensions.rows()
96      },
97      interface.elements(),
98      interface.root_id(),
99      &mut actions
100    );
101    let _ = interface.actions (actions);
102    interface
103  }
104
105  fn get_input (&mut self, input_buffer : &mut Vec <view::Input>) {
106    use view::{input, Input};
107    log::trace!("get input...");
108    input_buffer.extend (self.release_buttons.drain (..)
109      .map (|button|{
110        let input = Input::Button (button, input::button::State::Released);
111        log::debug!("input: {:?}", input);
112        input
113      }));
114    if let Some (pancurses_input) = self.inner.getch_nowait() {
115      log::trace!("pancurses input: {:?}", pancurses_input);
116      let input = match pancurses_input.into() {
117        // pancurses::Input::KeyResize is converted to
118        // input::System::Resize{0,0}
119        Input::System (input::System::Resized { width: 0, height: 0 }) => {
120          // get the current dimensions
121          let (width, height) = {
122            let (rows, columns) = self.inner.dimensions_rc();
123            debug_assert!(rows    >= 0);
124            debug_assert!(columns >= 0);
125            // NOTE: the curses display represents dimensions as (rows, columns)
126            // pairs, but the interface represents dimensions in (width,
127            // height), so these values are transposed when generating the
128            // resize event
129            (columns as u32, rows as u32)
130          };
131          input::System::Resized { width, height }.into()
132        }
133        Input::Button (button, pressed) => {
134          debug_assert_eq!(pressed, input::button::State::Pressed);
135          // release next frame
136          self.release_buttons.push (button);
137          Input::Button (button, pressed)
138        }
139        Input::Text (input::Text::Char (ch)) => {
140          // generate a button event for char input
141          let button = {
142            let (keycode, modifiers) = char_to_keycode (ch);
143            input::Button { variant: keycode.into(), modifiers }
144          };
145          let input = Input::Text (input::Text::Char (ch));
146          log::debug!("input: {:?}", input);
147          input_buffer.push (input);
148          // release next frame
149          self.release_buttons.push (button);
150          (button.clone(), input::button::State::Pressed).into()
151        }
152        input => input
153      };
154      log::debug!("input: {:?}", input);
155      input_buffer.push (input);
156    }
157    log::trace!("...get input");
158  }
159
160  fn display_view <V : AsRef <View>> (&mut self,
161    view_tree      : &Tree <V>,
162    _display_values : std::vec::Drain <(NodeId, view::Display)>
163  ) {
164    use std::collections::VecDeque;
165    use pancurses::chtype;
166    use nsys::curses::{color_pair_attr, pancurses_ok, pancurses_warn_ok};
167    use view::component::{canvas, Body, Canvas, Image, Kind};
168    log::trace!("display view...");
169    // trace info
170    let (attrs, colors) = self.inner.win().attrget();
171    let bkgd = self.inner.win().getbkgd();
172    log::trace!("  window: attributes: {:064b}", attrs);
173    log::trace!("  window: color pair: {}",   colors);
174    log::trace!("  window: background: {:064b}", bkgd);
175    // screen is the root canvas element
176    let screen_id     = view_tree.root_node_id().unwrap();
177    let screen        = view_tree.get (screen_id).unwrap().data().as_ref();
178    let screen_canvas = Canvas::try_ref (&screen.component).unwrap();
179    // set background and clear display if clear color is set
180    // TODO: this logic may need to rethinking
181    let (clear, bg_color) = {
182      let color = match screen_canvas.clear_color {
183        canvas::ClearColor::Appearance =>
184          screen.appearance.style.as_ref().map(|style| style.bg),
185        canvas::ClearColor::Fixed (clear_color) => clear_color
186      };
187      ( color.is_some(),
188        color.map(|color| {
189          let bg = convert_color (color).unwrap();
190          color_pair_attr (easycurses::Color::White, bg)
191        }).unwrap_or (0x00) // no color bits: color pair id 0 should be white/black
192      )
193    };
194    self.inner.win().bkgdset (' ' as chtype | bg_color);
195    if clear {
196      pancurses_ok!(self.inner.win().erase());
197    }
198    // initialize style
199    let style = screen.appearance.style.clone().unwrap_or_default();
200    // draw screen border if present
201    if let Some (border) = screen_canvas.border.as_ref() {
202      let rc_min = (0,0);
203      let rc_max = (
204        screen_canvas.coordinates.dimensions().vec().numcast().unwrap()
205          - Vector2::new (1,1)
206      ).into_tuple();
207      let color = color_pair_attr (
208        convert_color (style.fg).unwrap(),
209        convert_color (style.bg).unwrap());
210      pancurses_warn_ok!(self.inner.draw_border (
211        border.left         as chtype | color,
212        border.right        as chtype | color,
213        border.top          as chtype | color,
214        border.bottom       as chtype | color,
215        border.top_left     as chtype | color,
216        border.top_right    as chtype | color,
217        border.bottom_left  as chtype | color,
218        border.bottom_right as chtype | color,
219        rc_min, rc_max,
220        border.thickness_top    as u32,
221        border.thickness_bottom as u32,
222        border.thickness_left   as u32,
223        border.thickness_right  as u32
224      ));
225    }
226    // clip first-level child canvases and draw
227    let screen_aabb  = screen_canvas.coordinates.into();
228    let mut canvases = VecDeque::new();
229    for child_id in view_tree.children_ids (screen_id).unwrap() {
230      let child = view_tree.get (child_id).unwrap().data().as_ref();
231      let style = child.appearance.style.as_ref().unwrap_or (&style);
232      if let Some (child_canvas) = Canvas::try_ref (&child.component) {
233        if let Some ((canvas, body_aabb)) =
234          canvas_and_body_aabb (&child_canvas, &screen_aabb)
235        {
236          canvases.push_back ((child_id, canvas, style, body_aabb));
237        }
238      }
239    }
240    while canvases.len() > 0 {
241      let (node_id, canvas, style, body_aabb) = canvases.pop_front().unwrap();
242      let node = view_tree.get (node_id).unwrap();
243      let mut text  = None;
244      let mut image = None;
245      for child_id in node.children().iter().rev() {
246        let child = view_tree.get (child_id).unwrap().data().as_ref();
247        let style = child.appearance.style.as_ref().unwrap_or (&style);
248        if let Some (child_canvas) = Canvas::try_ref (&child.component) {
249          if let Some ((canvas, body_aabb)) =
250            canvas_and_body_aabb (&child_canvas, &screen_aabb)
251          {
252            canvases.push_front ((child_id, canvas, style, body_aabb));
253          }
254        } else if let Some (child_text) = Body::try_ref (&child.component) {
255          debug_assert!(text.is_none());
256          // NOTE: this slicing is unsafe if the string contains multi-byte
257          // characters
258          let skip  = body_aabb.min().0.x as usize;
259          let take  = body_aabb.max().0.x as usize - skip;
260          let start = body_aabb.min().0.y as usize;
261          let end   = body_aabb.max().0.y as usize;
262          text = Some ((
263            child_text.0.lines().skip (skip).take (take).map (
264              move |line| &line[start.min (line.len())..end.min (line.len())]),
265            child.appearance.style.clone()
266          ));
267        } else if let Some (child_image) = Image::try_ref (&child.component) {
268          debug_assert!(image.is_none());
269          match child_image {
270            Image::Raw64 (pixmap) => {
271              let skip  = body_aabb.min().0.x as usize;
272              let take  = body_aabb.max().0.x as usize - skip;
273              let start = body_aabb.min().0.y as usize;
274              let end   = body_aabb.max().0.y as usize;
275              image = Some (pixmap.rows().skip (skip).take (take).map (
276                move |row| &row[start.min (row.len())..end.min (row.len())]
277              ));
278            }
279            Image::Raw8(_) | Image::Raw16(_) | Image::Raw32(_) |
280            Image::Texture(_) => {
281              // TODO: image resource
282              image = None;
283            }
284          }
285        }
286      }
287      self.draw_canvas (&canvas, &style, text, image, true);
288    }
289    // TODO: win().refresh() ?
290    //self.inner.draw_border_default();  // TODO: debug
291    log::trace!("...display view");
292
293    fn canvas_and_body_aabb (child_canvas : &Canvas, screen_aabb : &Aabb2 <i32>)
294      -> Option <(Canvas, Aabb2 <i32>)>
295    {
296      let child_aabb = child_canvas.coordinates.into();
297      intersect::continuous_aabb2_aabb2 (screen_aabb, &child_aabb).map (
298        |intersection| {
299          let coordinates = view::Coordinates::tile_from_aabb (intersection);
300          let mut canvas  = Canvas {
301            coordinates, .. child_canvas.clone()
302          };
303          let (body_width, body_height) = child_canvas.body_wh();
304          let (mut body_min_row, mut body_min_col) = (0, 0);
305          let (mut body_max_row, mut body_max_col) =
306            (body_height as i32, body_width as i32);
307          if let Some (border) = canvas.border.as_mut() {
308            let outside_top = screen_aabb.min().0.x - child_aabb.min().0.x;
309            if outside_top > 0 {
310              body_min_row = (outside_top as u32)
311                .saturating_sub (border.thickness_top as u32) as i32;
312              border.thickness_top = border.thickness_top
313                .saturating_sub (outside_top    as u16);
314            }
315            let outside_bottom = child_aabb.max().0.x - screen_aabb.max().0.x;
316            if outside_bottom > 0 {
317              body_max_row = body_max_row - (outside_bottom as u32)
318                .saturating_sub (border.thickness_bottom as u32) as i32;
319              border.thickness_bottom = border.thickness_bottom
320                .saturating_sub (outside_bottom as u16);
321            }
322            let outside_left = screen_aabb.min().0.y - child_aabb.min().0.y;
323            if outside_left > 0 {
324              body_min_col = (outside_left as u32)
325                .saturating_sub (border.thickness_left as u32) as i32;
326              border.thickness_left   = border.thickness_left
327                .saturating_sub (outside_left   as u16);
328            }
329            let outside_right = child_aabb.max().0.y - screen_aabb.max().0.y;
330            if outside_right > 0 {
331              body_max_col = body_max_col - (outside_right as u32)
332                .saturating_sub (border.thickness_right as u32) as i32;
333              border.thickness_right  = border.thickness_right
334                .saturating_sub (outside_right  as u16);
335            }
336          }
337          ( canvas,
338            Aabb2::with_minmax (
339              [body_min_row, body_min_col].into(),
340              [body_max_row, body_max_col].into()
341            )
342          )
343        }
344      )
345    }
346  }
347}
348
349impl Curses {
350  // TODO: debug flag
351  fn draw_canvas <'a> (&mut self,
352    canvas : &view::component::Canvas,
353    style  : &view::Style,
354    text   : Option <(impl Iterator <Item=&'a str>, Option <view::Style>)>,
355    image  : Option <impl Iterator <Item=&'a [u64]>>,
356    _debug : bool
357  ) {
358    use std::convert::TryInto;
359    use nsys::curses::{chtype_color_pair, color_pair_attr, pancurses_ok,
360      pancurses_warn_ok};
361    use pancurses::chtype;
362    use crate::interface::view::component::canvas;
363    log::trace!("draw_canvas...");
364    let curses  = &mut self.inner;
365    let (position, dimensions)
366      : (view::position::Tile, view::dimensions::Tile)
367      = canvas.coordinates.try_into().unwrap();
368    let (rc_min, rc_max) = {
369      let (row,  col)  = (position.row(), position.column());
370      let (rows, cols) = (dimensions.rows() as i32, dimensions.columns() as i32);
371      ( (row, col),
372        (row + rows-1, col + cols-1) )
373    };
374    // clear
375    let clear_color = match canvas.clear_color {
376      canvas::ClearColor::Appearance    => Some (style.bg),
377      canvas::ClearColor::Fixed (color) => color
378    };
379    clear_color.map (|color|{
380      let border       = ' ' as chtype;
381      let bgcolor      = convert_color (color).unwrap();
382      let fill_color   = color_pair_attr (easycurses::Color::White, bgcolor);
383      let fill         = ' ' as chtype | fill_color;
384      curses.draw_rect (border, fill, rc_min, rc_max, 0,0);  // 0,0: no border
385    });
386    let color = color_pair_attr (
387      convert_color (style.fg).unwrap(),
388      convert_color (style.bg).unwrap());
389    let color_pair = chtype_color_pair (color);
390    pancurses_ok!(curses.win().color_set (color_pair));
391    curses.win().bkgdset (b' ' as chtype | color);
392    // border
393    let (border_thickness_top, border_thickness_left) =
394      canvas.border.as_ref().map (|border|{
395        pancurses_warn_ok!(curses.draw_border (
396          border.left         as chtype | color,
397          border.right        as chtype | color,
398          border.top          as chtype | color,
399          border.bottom       as chtype | color,
400          border.top_left     as chtype | color,
401          border.top_right    as chtype | color,
402          border.bottom_left  as chtype | color,
403          border.bottom_right as chtype | color,
404          rc_min, rc_max,
405          border.thickness_top    as u32,
406          border.thickness_bottom as u32,
407          border.thickness_left   as u32,
408          border.thickness_right  as u32
409        ));
410        ( border.thickness_top  as i32,
411          border.thickness_left as i32 )
412      }).unwrap_or_default();
413    // text
414    text.map (|(text, style)|{
415      style.map (|style|{
416        let color = color_pair_attr (
417          convert_color (style.fg).unwrap(),
418          convert_color (style.bg).unwrap());
419        let color_pair = chtype_color_pair (color);
420        pancurses_ok!(curses.win().color_set (color_pair));
421        curses.win().bkgdset (b' ' as chtype | color);
422      });
423      for (i, line) in text.enumerate() {
424        let printable = line.trim_start_matches ('\0');
425        let skip = line.len() - printable.len();
426        let row = rc_min.0 + border_thickness_top + i as i32;
427        let col = rc_min.1 + border_thickness_left + skip as i32;
428        if line.len() > 0 {
429          let _ = curses.win().mvaddstr (row, col, printable);
430        }
431      }
432    });
433    // image
434    image.map (|image|{
435      for (i, line) in image.enumerate() {
436        let row = rc_min.0 + border_thickness_top + i as i32;
437        let col = rc_min.1 + border_thickness_left;
438        for (j, ch) in line.iter().enumerate() {
439          if *ch != 0x0 {
440            let _ = curses.win().mvaddch (row, col+j as i32, *ch as chtype);
441          }
442        }
443      }
444    });
445    log::trace!("...draw_canvas");
446  }
447}
448
449impl From <pancurses::Input> for view::Input {
450
451  /// Convert from pancurses input to an input button (keycode). Note that
452  /// easycurses exports an alias of `pancurses::Input` as `easycurses::Input`.
453  ///
454  /// Note that some key combinations may not be captured or may be missing
455  /// modifiers.
456  fn from (input : pancurses::Input) -> Self {
457    use pancurses::Input::*;
458    use view::input::{self, button::Keycode, Modifiers};
459    const ALT   : Modifiers = Modifiers::ALT;
460    const CTRL  : Modifiers = Modifiers::CTRL;
461    const SHIFT : Modifiers = Modifiers::SHIFT;
462    const EMPTY : Modifiers = Modifiers::empty();
463    let unhandled = |input| {
464      log::warn!("curses unhandled input: {:?}", input);
465      (Keycode::NonConvert, EMPTY)
466    };
467    let (keycode, modifiers) = match input {
468      Character (ch) => match ch {
469        'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' |
470        'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' |
471        'y' | 'z' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0' |
472        '-' | '=' | '`' | '[' | ']' | '\\'| ';' | '\''| ',' | '.' | '/' | 'A' |
473        'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' |
474        'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' |
475        'Z' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')' | '_' |
476        '+' | '~' | '{' | '}' | '|' | ':' | '"' | '<' | '>' | '?' | ' ' | '\n'|
477        '\t'    => return view::input::Text::Char (ch).into(),
478        special => {
479          let unhandled = |ch| {
480            log::warn!("curses unhandled special character: {:?}", ch);
481            (Keycode::NonConvert, EMPTY)
482          };
483          let keyname = pancurses::keyname (special as i32).unwrap_or_else (||{
484            log::error!("pancurses keyname failed for special char {:?}",
485              special);
486            unreachable!()
487          });
488          log::trace!("keyname: {:?}", keyname);
489          match keyname.as_str() {
490            "^A" => (Keycode::A, CTRL),
491            "^B" => (Keycode::B, CTRL),
492            "^C" => (Keycode::C, CTRL),
493            "^D" => (Keycode::D, CTRL),
494            "^E" => (Keycode::E, CTRL),
495            "^F" => (Keycode::F, CTRL),
496            "^G" => (Keycode::G, CTRL),
497            // NOTE: on windows (pdcurses) backspace is emitted as CTRL+H
498            "^H" => (Keycode::Backspace, EMPTY),
499            "^I" => (Keycode::I, CTRL),
500            "^J" => (Keycode::J, CTRL),
501            "^K" => (Keycode::K, CTRL),
502            "^L" => (Keycode::L, CTRL),
503            "^M" => (Keycode::M, CTRL),
504            "^N" => (Keycode::N, CTRL),
505            "^O" => (Keycode::O, CTRL),
506            "^P" => (Keycode::P, CTRL),
507            "^Q" => (Keycode::Q, CTRL),
508            "^R" => (Keycode::R, CTRL),
509            "^S" => (Keycode::S, CTRL),
510            "^T" => (Keycode::T, CTRL),
511            "^U" => (Keycode::U, CTRL),
512            "^V" => (Keycode::V, CTRL),
513            "^W" => (Keycode::W, CTRL),
514            "^X" => (Keycode::X, CTRL),
515            "^Y" => (Keycode::Y, CTRL),
516            "^Z" => (Keycode::Z, CTRL),
517            "^_" => (Keycode::Minus, SHIFT | CTRL),
518            "^?" => (Keycode::Backspace,   EMPTY),
519            "^[" => (Keycode::Escape, EMPTY), // note does not capture modifiers
520            // note this is generated by other key combinations, e.g. Ctrl+Space
521            "^@" => (Keycode::Key2, CTRL),
522            // PDCurses
523            "KEY_PAUSE"      => (Keycode::Pause,  EMPTY),
524            "KEY_SCROLLLOCK" => (Keycode::ScrollLock, EMPTY),
525            "KEY_SDOWN"      => (Keycode::Down,   SHIFT),
526            "KEY_SUP"        => (Keycode::Up,     SHIFT),
527            "KEY_B2"         => (Keycode::Numpad5, SHIFT),
528            "SHF_PADENTER"   => (Keycode::NumpadEnter, SHIFT),
529            "SHF_PADMINUS"   => (Keycode::NumpadSubtract, SHIFT),
530            "SHF_PADPLUS"    => (Keycode::NumpadAdd,    SHIFT),
531            "SHF_PADSLASH"   => (Keycode::NumpadDivide, SHIFT),
532            "SHF_PADSTAR"    => (Keycode::NumpadMultiply, SHIFT),
533            "KEY_F(25)"      => (Keycode::F1,     CTRL),
534            "KEY_F(26)"      => (Keycode::F2,     CTRL),
535            "KEY_F(27)"      => (Keycode::F3,     CTRL),
536            "KEY_F(28)"      => (Keycode::F4,     CTRL),
537            "KEY_F(29)"      => (Keycode::F5,     CTRL),
538            "KEY_F(30)"      => (Keycode::F6,     CTRL),
539            "KEY_F(31)"      => (Keycode::F7,     CTRL),
540            "KEY_F(32)"      => (Keycode::F8,     CTRL),
541            "KEY_F(33)"      => (Keycode::F9,     CTRL),
542            "KEY_F(34)"      => (Keycode::F10,    CTRL),
543            "KEY_F(35)"      => (Keycode::F11,    CTRL),
544            "KEY_F(36)"      => (Keycode::F12,    CTRL),
545            "KEY_F(37)"      => (Keycode::F1,     ALT),
546            "KEY_F(38)"      => (Keycode::F2,     ALT),
547            "KEY_F(39)"      => (Keycode::F3,     ALT),
548            "KEY_F(40)"      => (Keycode::F4,     ALT),
549            "KEY_F(41)"      => (Keycode::F5,     ALT),
550            "KEY_F(42)"      => (Keycode::F6,     ALT),
551            "KEY_F(43)"      => (Keycode::F7,     ALT),
552            "KEY_F(44)"      => (Keycode::F8,     ALT),
553            "KEY_F(45)"      => (Keycode::F9,     ALT),
554            "KEY_F(46)"      => (Keycode::F10,    ALT),
555            "KEY_F(47)"      => (Keycode::F11,    ALT),
556            "KEY_F(48)"      => (Keycode::F12,    ALT),
557            "CTL_0"          => (Keycode::Key0,   CTRL),
558            "CTL_1"          => (Keycode::Key1,   CTRL),
559            "CTL_2"          => (Keycode::Key2,   CTRL),
560            "CTL_3"          => (Keycode::Key3,   CTRL),
561            "CTL_4"          => (Keycode::Key4,   CTRL),
562            "CTL_5"          => (Keycode::Key5,   CTRL),
563            "CTL_6"          => (Keycode::Key6,   CTRL),
564            "CTL_7"          => (Keycode::Key7,   CTRL),
565            "CTL_8"          => (Keycode::Key8,   CTRL),
566            "CTL_9"          => (Keycode::Key9,   CTRL),
567            "CTL_DOWN"       => (Keycode::Down,   CTRL),
568            "CTL_LEFT"       => (Keycode::Left,   CTRL),
569            "CTL_RIGHT"      => (Keycode::Right,  CTRL),
570            "CTL_UP"         => (Keycode::Up,     CTRL),
571            "CTL_PAD0"       => (Keycode::Numpad0, CTRL),
572            "CTL_PAD1"       => (Keycode::Numpad1, CTRL),
573            "CTL_PAD2"       => (Keycode::Numpad2, CTRL),
574            "CTL_PAD3"       => (Keycode::Numpad3, CTRL),
575            "CTL_PAD4"       => (Keycode::Numpad4, CTRL),
576            "CTL_PAD5"       => (Keycode::Numpad5, CTRL),
577            "CTL_PAD6"       => (Keycode::Numpad6, CTRL),
578            "CTL_PAD7"       => (Keycode::Numpad7, CTRL),
579            "CTL_PAD8"       => (Keycode::Numpad8, CTRL),
580            "CTL_PAD9"       => (Keycode::Numpad9, CTRL),
581            "CTL_PADENTER"   => (Keycode::NumpadEnter, CTRL),
582            "CTL_PADMINUS"   => (Keycode::NumpadSubtract, CTRL),
583            "CTL_PADPLUS"    => (Keycode::NumpadAdd,  CTRL),
584            "CTL_PADSLASH"   => (Keycode::NumpadDivide, CTRL),
585            "CTL_PADSTOP"    => (Keycode::NumpadDecimal, CTRL),
586            "CTL_PADSTAR"    => (Keycode::NumpadMultiply, CTRL),
587            "CTL_BQUOTE"     => (Keycode::Grave,  CTRL),
588            "CTL_DEL"        => (Keycode::Delete, CTRL),
589            "CTL_END"        => (Keycode::End,    CTRL),
590            "CTL_FLASH"      => (Keycode::Slash,  CTRL),
591            "CTL_HOME"       => (Keycode::Home,   CTRL),
592            "CTL_INS"        => (Keycode::Insert, CTRL),
593            "CTL_PAUSE"      => (Keycode::Pause,  CTRL),
594            "CTL_PGDN"       => (Keycode::PageDown, CTRL),
595            "CTL_PGUP"       => (Keycode::PageUp, CTRL),
596            "CTL_SEMICOLON"  => (Keycode::Semicolon, CTRL),
597            "CTL_STOP"       => (Keycode::Period, CTRL),
598            "CTL_TAB"        => (Keycode::Tab,    CTRL),
599            "ALT_0"          => (Keycode::Key0, ALT),
600            "ALT_1"          => (Keycode::Key1, ALT),
601            "ALT_2"          => (Keycode::Key2, ALT),
602            "ALT_3"          => (Keycode::Key3, ALT),
603            "ALT_4"          => (Keycode::Key4, ALT),
604            "ALT_5"          => (Keycode::Key5, ALT),
605            "ALT_6"          => (Keycode::Key6, ALT),
606            "ALT_7"          => (Keycode::Key7, ALT),
607            "ALT_8"          => (Keycode::Key8, ALT),
608            "ALT_9"          => (Keycode::Key9, ALT),
609            "ALT_A"          => (Keycode::A, ALT),
610            "ALT_B"          => (Keycode::B, ALT),
611            "ALT_C"          => (Keycode::C, ALT),
612            "ALT_D"          => (Keycode::D, ALT),
613            "ALT_E"          => (Keycode::E, ALT),
614            "ALT_F"          => (Keycode::F, ALT),
615            "ALT_G"          => (Keycode::G, ALT),
616            "ALT_H"          => (Keycode::H, ALT),
617            "ALT_I"          => (Keycode::I, ALT),
618            "ALT_J"          => (Keycode::J, ALT),
619            "ALT_K"          => (Keycode::K, ALT),
620            "ALT_L"          => (Keycode::L, ALT),
621            "ALT_M"          => (Keycode::M, ALT),
622            "ALT_N"          => (Keycode::N, ALT),
623            "ALT_O"          => (Keycode::O, ALT),
624            "ALT_P"          => (Keycode::P, ALT),
625            "ALT_Q"          => (Keycode::Q, ALT),
626            "ALT_R"          => (Keycode::R, ALT),
627            "ALT_S"          => (Keycode::S, ALT),
628            "ALT_T"          => (Keycode::T, ALT),
629            "ALT_U"          => (Keycode::U, ALT),
630            "ALT_V"          => (Keycode::V, ALT),
631            "ALT_W"          => (Keycode::W, ALT),
632            "ALT_X"          => (Keycode::X, ALT),
633            "ALT_Y"          => (Keycode::Y, ALT),
634            "ALT_Z"          => (Keycode::Z, ALT),
635            "ALT_BKSP"       => (Keycode::Backspace,   ALT),
636            "ALT_BQUOTE"     => (Keycode::Grave,  ALT),
637            "ALT_BSLASH"     => (Keycode::Backslash, ALT),
638            "^\\"            => (Keycode::LBracket, ALT),
639            "ALT_COMMA"      => (Keycode::Comma, ALT),
640            "ALT_DEL"        => (Keycode::Delete, ALT),
641            "ALT_END"        => (Keycode::End,    ALT),
642            "ALT_ENTER"      => (Keycode::Enter, ALT),
643            "ALT_EQUALS"     => (Keycode::Equal, ALT),
644            "ALT_HOME"       => (Keycode::Home,   ALT),
645            "ALT_INS"        => (Keycode::Insert, ALT),
646            "ALT_FQUOTE"     => (Keycode::Quote, ALT),
647            "ALT_FSLASH"     => (Keycode::Slash, ALT),
648            "ALT_MINUS"      => (Keycode::Minus, ALT),
649            "ALT_PGDN"       => (Keycode::PageDown, ALT),
650            "ALT_PGUP"       => (Keycode::PageUp, ALT),
651            "ALT_RBRACKET"   => (Keycode::RBracket, ALT),
652            "ALT_SCROLLLOCK" => (Keycode::ScrollLock, ALT),
653            "ALT_SEMICOLON"  => (Keycode::Semicolon, ALT),
654            "ALT_STOP"       => (Keycode::Period, ALT),
655            "ALT_PADENTER"   => (Keycode::NumpadEnter, ALT),
656            "ALT_PADMINUS"   => (Keycode::NumpadSubtract, ALT),
657            "ALT_PADSLASH"   => (Keycode::NumpadDivide, ALT),
658            "ALT_PADSTAR"    => (Keycode::NumpadMultiply, ALT),
659            "ALT_PADSTOP"    => (Keycode::NumpadDecimal, ALT),
660            ch => unhandled (ch)
661          }
662        }
663      },
664      Unknown(_) => unhandled (input),
665      KeyCodeYes => unhandled (input),
666
667      KeyBreak => (Keycode::Pause, EMPTY),
668      KeyDown => (Keycode::Down, EMPTY),
669      KeyUp => (Keycode::Up, EMPTY),
670      KeyLeft => (Keycode::Left, EMPTY),
671      KeyRight => (Keycode::Right, EMPTY),
672      KeyHome => (Keycode::Home, EMPTY),
673      KeyBackspace => (Keycode::Backspace, EMPTY),
674      KeyF0 => unhandled (input),
675      KeyF1 => (Keycode::F1, EMPTY),
676      KeyF2 => (Keycode::F2, EMPTY),
677      KeyF3 => (Keycode::F3, EMPTY),
678      KeyF4 => (Keycode::F4, EMPTY),
679      KeyF5 => (Keycode::F5, EMPTY),
680      KeyF6 => (Keycode::F6, EMPTY),
681      KeyF7 => (Keycode::F7, EMPTY),
682      KeyF8 => (Keycode::F8, EMPTY),
683      KeyF9 => (Keycode::F9, EMPTY),
684      KeyF10 => (Keycode::F10, EMPTY),
685      KeyF11 => (Keycode::F11, EMPTY),
686      KeyF12 => (Keycode::F12, EMPTY),
687      KeyF13 => (Keycode::F13, EMPTY),
688      KeyF14 => (Keycode::F14, EMPTY),
689      KeyF15 => (Keycode::F15, EMPTY),
690
691      KeyDL => unhandled (input),
692      KeyIL => unhandled (input),
693      KeyDC => (Keycode::Delete, EMPTY),
694      KeyIC => (Keycode::Insert, EMPTY),
695      KeyEIC => unhandled (input),
696      KeyClear => unhandled (input),
697      KeyEOS => unhandled (input),
698      KeyEOL => unhandled (input),
699      KeySF => (Keycode::Down, SHIFT),
700      KeySR => (Keycode::Up, SHIFT),
701      KeyNPage => (Keycode::PageDown, EMPTY),
702      KeyPPage => (Keycode::PageUp, EMPTY),
703      KeySTab => unhandled (input),  // shift+tab generates KeyBTab
704      KeyCTab => unhandled (input),
705      KeyCATab => unhandled (input),
706      KeyEnter => (Keycode::Enter, EMPTY),
707      KeySReset => unhandled (input),
708      KeyReset => unhandled (input),
709      KeyPrint => unhandled (input),
710      KeyLL => unhandled (input),
711      KeyAbort => unhandled (input),
712      KeySHelp => unhandled (input),
713      KeyLHelp => unhandled (input),
714      KeyBTab => (Keycode::Tab, SHIFT),
715      KeyBeg => unhandled (input),
716      KeyCancel => unhandled (input),
717      KeyClose => unhandled (input),
718      KeyCommand => unhandled (input),
719      KeyCopy => (Keycode::Copy, EMPTY),
720      KeyCreate => unhandled (input),
721      KeyEnd => (Keycode::End, EMPTY),
722      KeyExit => unhandled (input),
723      KeyFind => unhandled (input),
724      KeyHelp => unhandled (input),
725      KeyMark => unhandled (input),
726      KeyMessage => unhandled (input),
727      KeyMove => unhandled (input),
728      KeyNext => unhandled (input),
729      KeyOpen => unhandled (input),
730      KeyOptions => unhandled (input),
731      KeyPrevious => unhandled (input),
732      KeyRedo => unhandled (input),
733      KeyReference => unhandled (input),
734      KeyRefresh => unhandled (input),
735      KeyReplace => unhandled (input),
736      KeyRestart => unhandled (input),
737      KeyResume => unhandled (input),
738      KeySave => unhandled (input),
739      KeySBeg => unhandled (input),
740      KeySCancel => unhandled (input),
741      KeySCommand => unhandled (input),
742      KeySCopy => unhandled (input),
743      KeySCreate => unhandled (input),
744      KeySDC => (Keycode::Delete, SHIFT),
745      KeySDL => unhandled (input),
746      KeySelect => unhandled (input),
747      KeySEnd => (Keycode::End, SHIFT),
748      KeySEOL => unhandled (input),
749      KeySExit => unhandled (input),
750      KeySFind => unhandled (input),
751      KeySHome => (Keycode::Home, SHIFT),
752      KeySIC => (Keycode::Insert, SHIFT),
753
754      // PDCurses produces KeySMessage on Shift+Left
755      KeySLeft | KeySMessage => (Keycode::Left, SHIFT),
756      KeySMove => unhandled (input),
757      KeySNext => (Keycode::PageDown, SHIFT),
758      KeySOptions => unhandled (input),
759      KeySPrevious => (Keycode::PageUp, SHIFT),
760      KeySPrint => unhandled (input),
761      KeySRedo => unhandled (input),
762      KeySReplace => unhandled (input),
763      // PDCurses produces KeySResume on Shift+Right
764      KeySRight | KeySResume => (Keycode::Right, SHIFT),
765      KeySSave => unhandled (input),
766      KeySSuspend => unhandled (input),
767      KeySUndo => unhandled (input),
768      KeySuspend => unhandled (input),
769      KeyUndo => unhandled (input),
770
771      // NOTE: because we don't have access to the pancurses window in this
772      // scope, we return a resize input event with zero dimensions; the
773      // get_input() impl will take care of setting the correct size before
774      // returning the event
775      KeyResize => return input::System::Resized { width: 0, height: 0 }.into(),
776      KeyEvent => unhandled (input),
777      KeyMouse => unhandled (input),
778
779      KeyA1 => unhandled (input),
780      KeyA3 => unhandled (input),
781      KeyB2 => unhandled (input),
782      KeyC1 => unhandled (input),
783      KeyC3 => unhandled (input)
784    };
785    ( input::Button {
786        variant: keycode.into(),
787        modifiers
788      },
789      input::button::State::Pressed
790    ).into()
791  }
792}
793
794/// Converts a character to a keycode+modifier. The modifier is 'SHIFT' if the
795/// character is uppercase or requires pressing shift to type under usual
796/// circumstances (e.g. '!'), and empty otherwise.
797fn char_to_keycode (ch : char)
798  -> (view::input::button::Keycode, view::input::Modifiers)
799{
800  use view::input::Modifiers;
801  use view::input::button::Keycode;
802  const EMPTY : Modifiers = Modifiers::empty();
803  const SHIFT : Modifiers = Modifiers::SHIFT;
804  match ch {
805    'a' => (Keycode::A, EMPTY),
806    'b' => (Keycode::B, EMPTY),
807    'c' => (Keycode::C, EMPTY),
808    'd' => (Keycode::D, EMPTY),
809    'e' => (Keycode::E, EMPTY),
810    'f' => (Keycode::F, EMPTY),
811    'g' => (Keycode::G, EMPTY),
812    'h' => (Keycode::H, EMPTY),
813    'i' => (Keycode::I, EMPTY),
814    'j' => (Keycode::J, EMPTY),
815    'k' => (Keycode::K, EMPTY),
816    'l' => (Keycode::L, EMPTY),
817    'm' => (Keycode::M, EMPTY),
818    'n' => (Keycode::N, EMPTY),
819    'o' => (Keycode::O, EMPTY),
820    'p' => (Keycode::P, EMPTY),
821    'q' => (Keycode::Q, EMPTY),
822    'r' => (Keycode::R, EMPTY),
823    's' => (Keycode::S, EMPTY),
824    't' => (Keycode::T, EMPTY),
825    'u' => (Keycode::U, EMPTY),
826    'v' => (Keycode::V, EMPTY),
827    'w' => (Keycode::W, EMPTY),
828    'x' => (Keycode::X, EMPTY),
829    'y' => (Keycode::Y, EMPTY),
830    'z' => (Keycode::Z, EMPTY),
831    '1' => (Keycode::Key1, EMPTY),
832    '2' => (Keycode::Key2, EMPTY),
833    '3' => (Keycode::Key3, EMPTY),
834    '4' => (Keycode::Key4, EMPTY),
835    '5' => (Keycode::Key5, EMPTY),
836    '6' => (Keycode::Key6, EMPTY),
837    '7' => (Keycode::Key7, EMPTY),
838    '8' => (Keycode::Key8, EMPTY),
839    '9' => (Keycode::Key9, EMPTY),
840    '0' => (Keycode::Key0, EMPTY),
841    '-' => (Keycode::Minus, EMPTY),
842    '=' => (Keycode::Equal, EMPTY),
843    '`' => (Keycode::Grave, EMPTY),
844    '[' => (Keycode::LBracket, EMPTY),
845    ']' => (Keycode::RBracket, EMPTY),
846    '\\' => (Keycode::Backslash, EMPTY),
847    ';' => (Keycode::Semicolon, EMPTY),
848    '\'' => (Keycode::Quote, EMPTY),
849    ',' => (Keycode::Comma, EMPTY),
850    '.' => (Keycode::Period, EMPTY),
851    '/' => (Keycode::Slash, EMPTY),
852    'A' => (Keycode::A, SHIFT),
853    'B' => (Keycode::B, SHIFT),
854    'C' => (Keycode::C, SHIFT),
855    'D' => (Keycode::D, SHIFT),
856    'E' => (Keycode::E, SHIFT),
857    'F' => (Keycode::F, SHIFT),
858    'G' => (Keycode::G, SHIFT),
859    'H' => (Keycode::H, SHIFT),
860    'I' => (Keycode::I, SHIFT),
861    'J' => (Keycode::J, SHIFT),
862    'K' => (Keycode::K, SHIFT),
863    'L' => (Keycode::L, SHIFT),
864    'M' => (Keycode::M, SHIFT),
865    'N' => (Keycode::N, SHIFT),
866    'O' => (Keycode::O, SHIFT),
867    'P' => (Keycode::P, SHIFT),
868    'Q' => (Keycode::Q, SHIFT),
869    'R' => (Keycode::R, SHIFT),
870    'S' => (Keycode::S, SHIFT),
871    'T' => (Keycode::T, SHIFT),
872    'U' => (Keycode::U, SHIFT),
873    'V' => (Keycode::V, SHIFT),
874    'W' => (Keycode::W, SHIFT),
875    'X' => (Keycode::X, SHIFT),
876    'Y' => (Keycode::Y, SHIFT),
877    'Z' => (Keycode::Z, SHIFT),
878    '!' => (Keycode::Key1, SHIFT),
879    '@' => (Keycode::Key2, SHIFT),
880    '#' => (Keycode::Key3, SHIFT),
881    '$' => (Keycode::Key4, SHIFT),
882    '%' => (Keycode::Key5, SHIFT),
883    '^' => (Keycode::Key6, SHIFT),
884    '&' => (Keycode::Key7, SHIFT),
885    '*' => (Keycode::Key8, SHIFT),
886    '(' => (Keycode::Key9, SHIFT),
887    ')' => (Keycode::Key0, SHIFT),
888    '_' => (Keycode::Minus, SHIFT),
889    '+' => (Keycode::Equal, SHIFT),
890    '~' => (Keycode::Grave, SHIFT),
891    '{' => (Keycode::LBracket, SHIFT),
892    '}' => (Keycode::RBracket, SHIFT),
893    '|' => (Keycode::Backslash, SHIFT),
894    ':' => (Keycode::Semicolon, SHIFT),
895    '"' => (Keycode::Quote, SHIFT),
896    '<' => (Keycode::Comma, SHIFT),
897    '>' => (Keycode::Period, SHIFT),
898    '?' => (Keycode::Slash, SHIFT),
899    ' ' => (Keycode::Space, EMPTY),
900    '\n' => (Keycode::Enter, EMPTY),
901    '\t' => (Keycode::Tab, EMPTY),
902    _ => unreachable!()
903  }
904}
905
906fn convert_color (color : view::Color)
907  -> Result <easycurses::Color, view::Color>
908{
909  use view::color::*;
910  let color = match color {
911    Color::Named (named) => match named {
912      Named::Monochrome (Monochrome::White)  => easycurses::Color::White,
913      Named::Monochrome (Monochrome::Grey)   => return Err (color),
914      Named::Monochrome (Monochrome::Black)  => easycurses::Color::Black,
915      Named::Hue (hue) => match hue {
916        Hue::Primary    (Primary::Red)       => easycurses::Color::Red,
917        Hue::Primary    (Primary::Green)     => easycurses::Color::Green,
918        Hue::Primary    (Primary::Blue)      => easycurses::Color::Blue,
919        Hue::Secondary  (Secondary::Cyan)    => easycurses::Color::Cyan,
920        Hue::Secondary  (Secondary::Yellow)  => easycurses::Color::Yellow,
921        Hue::Secondary  (Secondary::Magenta) => easycurses::Color::Magenta,
922        _ => return Err (color)
923      }
924    },
925    _ => return Err (color)
926  };
927  Ok (color)
928}