Mostly add pancurses support. Test pancurses support on Travis

This commit is contained in:
nabijaczleweli 2016-10-10 00:48:51 +02:00
parent 9871e12734
commit 01951aa213
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
7 changed files with 299 additions and 22 deletions

View File

@ -3,3 +3,9 @@ cache: cargo
rust:
- stable
- nightly
script:
- cargo build --verbose
- cargo test --verbose
-
- cargo build --verbose --features=pancurses --no-default-features
- cargo test --verbose --features=pancurses --no-default-features

View File

@ -14,6 +14,12 @@ version = "0.3.1"
[build-dependencies]
skeptic = "0.6"
[features]
cursive_ncurses = ["ncurses"]
cursive_pancurses = ["pancurses"]
cursive_termion = ["termion"]
default = ["cursive_ncurses"]
[dependencies]
odds = "0.2"
toml = "0.2"
@ -21,20 +27,22 @@ unicode-segmentation = "0.1"
unicode-width = "0.1"
[dependencies.ncurses]
version = "5.83.0"
features = ["wide"]
optional = true
version = "5.83.0"
[dependencies.termion]
[dependencies.pancurses]
version = "0.3.0"
optional = true
[dependencies.termion]
version = "1.1.1"
optional = true
[dev-dependencies]
rand = "0.3"
skeptic = "0.6"
[features]
default = ["ncurses"]
[lib]
name = "cursive"

40
src/backend/curses/mod.rs Normal file
View File

@ -0,0 +1,40 @@
use theme::{BaseColor, Color};
#[cfg(feature = "cursive_ncurses")]
mod n;
#[cfg(feature = "cursive_ncurses")]
pub use self::n::*;
#[cfg(feature = "cursive_pancurses")]
mod pan;
#[cfg(feature = "cursive_pancurses")]
pub use self::pan::*;
fn find_closest(color: &Color) -> u8 {
match *color {
Color::Dark(BaseColor::Black) => 0,
Color::Dark(BaseColor::Red) => 1,
Color::Dark(BaseColor::Green) => 2,
Color::Dark(BaseColor::Yellow) => 3,
Color::Dark(BaseColor::Blue) => 4,
Color::Dark(BaseColor::Magenta) => 5,
Color::Dark(BaseColor::Cyan) => 6,
Color::Dark(BaseColor::White) => 7,
Color::Light(BaseColor::Black) => 8,
Color::Light(BaseColor::Red) => 9,
Color::Light(BaseColor::Green) => 10,
Color::Light(BaseColor::Yellow) => 11,
Color::Light(BaseColor::Blue) => 12,
Color::Light(BaseColor::Magenta) => 13,
Color::Light(BaseColor::Cyan) => 14,
Color::Light(BaseColor::White) => 15,
Color::Rgb(r, g, b) => {
let r = 6 * r as u16 / 256;
let g = 6 * g as u16 / 256;
let b = 6 * b as u16 / 256;
(16 + 36 * r + 6 * g + b) as u8
}
Color::RgbLowRes(r, g, b) => (16 + 36 * r + 6 * g + b) as u8,
}
}

View File

@ -105,7 +105,6 @@ impl backend::Backend for NcursesBackend {
/// Returns the Key enum corresponding to the given ncurses event.
fn parse_ncurses_char(ch: i32) -> Event {
match ch {
// Value sent by ncurses when nothing happens
-1 => Event::Refresh,

221
src/backend/curses/pan.rs Normal file
View File

@ -0,0 +1,221 @@
use backend;
use event::{Event, Key};
use pancurses;
use self::super::find_closest;
use theme::{BaseColor, Color, ColorStyle, Effect};
use utf8;
pub struct PancursesBackend {
window: pancurses::Window,
}
impl backend::Backend for PancursesBackend {
fn init() -> Self {
::std::env::set_var("ESCDELAY", "25");
let window = pancurses::initscr();
window.keypad(true);
pancurses::noecho();
pancurses::cbreak();
pancurses::start_color();
pancurses::curs_set(0);
window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as u64));
PancursesBackend { window: window }
}
fn screen_size(&self) -> (usize, usize) {
let (x, y) = self.window.get_max_yx();
(x as usize, y as usize)
}
fn has_colors(&self) -> bool {
pancurses::has_colors()
}
fn finish(&mut self) {
pancurses::endwin();
}
fn init_color_style(&mut self, style: ColorStyle, foreground: &Color,
background: &Color) {
pancurses::init_pair(style.id(),
find_closest(foreground) as i16,
find_closest(background) as i16);
}
fn with_color<F: FnOnce()>(&self, color: ColorStyle, f: F) {
// TODO: pancurses doesn't have an `attr_get` equivalent
// let mut current_style: pancurses::attr_t = 0;
// let mut current_color: i16 = 0;
// pancurses::attr_get(&mut current_style, &mut current_color);
let style = pancurses::COLOR_PAIR(color.id() as u64);
self.window.attron(style);
f();
self.window.attroff(style);
// pancurses::attron(current_style);
}
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
let style = match effect {
// A_REVERSE, taken from ncurses
Effect::Reverse => 1 << (10 + 8u32),
Effect::Simple => pancurses::A_NORMAL,
};
self.window.attron(style);
f();
self.window.attroff(style);
}
fn clear(&self) {
self.window.clear();
}
fn refresh(&self) {
self.window.refresh();
}
fn print_at(&self, (x, y): (usize, usize), text: &str) {
self.window.mvaddstr(y as i32, x as i32, text);
}
fn poll_event(&self) -> Event {
// TODO: there seems to not be any indication
// of Ctrl/Alt/Shift in these :v
if let Some(ev) = self.window.getch() {
match ev {
pancurses::Input::Character(c) => Event::Char(c),
pancurses::Input::Unknown(i) => Event::Unknown(i),
// TODO: I honestly have no fucking idea what KeyCodeYes is
pancurses::Input::KeyCodeYes => Event::Refresh,
// TODO: There seems to not be a Key::Break??
pancurses::Input::KeyBreak => Event::Refresh,
pancurses::Input::KeyDown => Event::Key(Key::Down),
pancurses::Input::KeyUp => Event::Key(Key::Up),
pancurses::Input::KeyLeft => Event::Key(Key::Left),
pancurses::Input::KeyRight => Event::Key(Key::Right),
pancurses::Input::KeyHome => Event::Key(Key::Home),
pancurses::Input::KeyBackspace => Event::Key(Key::Backspace),
// TODO: there seems to not be an F0 key for some reason?
pancurses::Input::KeyF0 => Event::Refresh,
pancurses::Input::KeyF1 => Event::Key(Key::F1),
pancurses::Input::KeyF2 => Event::Key(Key::F2),
pancurses::Input::KeyF3 => Event::Key(Key::F3),
pancurses::Input::KeyF4 => Event::Key(Key::F4),
pancurses::Input::KeyF5 => Event::Key(Key::F5),
pancurses::Input::KeyF6 => Event::Key(Key::F6),
pancurses::Input::KeyF7 => Event::Key(Key::F7),
pancurses::Input::KeyF8 => Event::Key(Key::F8),
pancurses::Input::KeyF9 => Event::Key(Key::F9),
pancurses::Input::KeyF10 => Event::Key(Key::F10),
pancurses::Input::KeyF11 => Event::Key(Key::F11),
pancurses::Input::KeyF12 => Event::Key(Key::F12),
// TODO: there seems to not be any F-keys above F15
// for some reason?
pancurses::Input::KeyF13 => Event::Refresh,
pancurses::Input::KeyF14 => Event::Refresh,
pancurses::Input::KeyF15 => Event::Refresh,
// TODO: not sure what those are
pancurses::Input::KeyDL => Event::Refresh,
pancurses::Input::KeyIL => Event::Refresh,
pancurses::Input::KeyDC => Event::Refresh,
pancurses::Input::KeyIC => Event::Refresh,
pancurses::Input::KeyEIC => Event::Refresh,
pancurses::Input::KeyClear => Event::Refresh,
pancurses::Input::KeyEOS => Event::Refresh,
pancurses::Input::KeyEOL => Event::Refresh,
pancurses::Input::KeySF => Event::Refresh,
pancurses::Input::KeySR => Event::Refresh,
pancurses::Input::KeyNPage => Event::Refresh,
pancurses::Input::KeyPPage => Event::Refresh,
// TODO: no way to handle this
pancurses::Input::KeySTab => Event::Refresh,
pancurses::Input::KeyCTab => Event::CtrlChar('\t'),
pancurses::Input::KeyCATab => Event::Refresh,
pancurses::Input::KeyEnter => Event::Key(Key::Enter),
// TODO: not sure what those are
pancurses::Input::KeySReset => Event::Refresh,
pancurses::Input::KeyReset => Event::Refresh,
pancurses::Input::KeyPrint => Event::Refresh,
pancurses::Input::KeyLL => Event::Refresh,
pancurses::Input::KeyAbort => Event::Refresh,
pancurses::Input::KeySHelp => Event::Refresh,
pancurses::Input::KeyLHelp => Event::Refresh,
pancurses::Input::KeyBTab => Event::Refresh,
pancurses::Input::KeyBeg => Event::Refresh,
pancurses::Input::KeyCancel => Event::Refresh,
pancurses::Input::KeyClose => Event::Refresh,
pancurses::Input::KeyCommand => Event::Refresh,
pancurses::Input::KeyCopy => Event::Refresh,
pancurses::Input::KeyCreate => Event::Refresh,
pancurses::Input::KeyEnd => Event::Refresh,
pancurses::Input::KeyExit => Event::Refresh,
pancurses::Input::KeyFind => Event::Refresh,
pancurses::Input::KeyHelp => Event::Refresh,
pancurses::Input::KeyMark => Event::Refresh,
pancurses::Input::KeyMessage => Event::Refresh,
pancurses::Input::KeyMove => Event::Refresh,
pancurses::Input::KeyNext => Event::Refresh,
pancurses::Input::KeyOpen => Event::Refresh,
pancurses::Input::KeyOptions => Event::Refresh,
pancurses::Input::KeyPrevious => Event::Refresh,
pancurses::Input::KeyRedo => Event::Refresh,
pancurses::Input::KeyReference => Event::Refresh,
pancurses::Input::KeyRefresh => Event::Refresh,
pancurses::Input::KeyReplace => Event::Refresh,
pancurses::Input::KeyRestart => Event::Refresh,
pancurses::Input::KeyResume => Event::Refresh,
pancurses::Input::KeySave => Event::Refresh,
pancurses::Input::KeySBeg => Event::Refresh,
pancurses::Input::KeySCancel => Event::Refresh,
pancurses::Input::KeySCommand => Event::Refresh,
pancurses::Input::KeySCopy => Event::Refresh,
pancurses::Input::KeySCreate => Event::Refresh,
pancurses::Input::KeySDC => Event::Refresh,
pancurses::Input::KeySDL => Event::Refresh,
pancurses::Input::KeySelect => Event::Refresh,
pancurses::Input::KeySEnd => Event::Refresh,
pancurses::Input::KeySEOL => Event::Refresh,
pancurses::Input::KeySExit => Event::Refresh,
pancurses::Input::KeySFind => Event::Refresh,
pancurses::Input::KeySHome => Event::Refresh,
pancurses::Input::KeySIC => Event::Refresh,
pancurses::Input::KeySLeft => Event::Refresh,
pancurses::Input::KeySMessage => Event::Refresh,
pancurses::Input::KeySMove => Event::Refresh,
pancurses::Input::KeySNext => Event::Refresh,
pancurses::Input::KeySOptions => Event::Refresh,
pancurses::Input::KeySPrevious => Event::Refresh,
pancurses::Input::KeySPrint => Event::Refresh,
pancurses::Input::KeySRedo => Event::Refresh,
pancurses::Input::KeySReplace => Event::Refresh,
pancurses::Input::KeySRight => Event::Refresh,
pancurses::Input::KeySResume => Event::Refresh,
pancurses::Input::KeySSave => Event::Refresh,
pancurses::Input::KeySSuspend => Event::Refresh,
pancurses::Input::KeySUndo => Event::Refresh,
pancurses::Input::KeySuspend => Event::Refresh,
pancurses::Input::KeyUndo => Event::Refresh,
pancurses::Input::KeyResize => Event::Refresh,
pancurses::Input::KeyEvent => Event::Refresh,
// TODO: not sure how to handle this
pancurses::Input::KeyMouse => Event::Refresh,
// TODO: not sure what those are
pancurses::Input::KeyA1 => Event::Refresh,
pancurses::Input::KeyA3 => Event::Refresh,
pancurses::Input::KeyB2 => Event::Refresh,
pancurses::Input::KeyC1 => Event::Refresh,
pancurses::Input::KeyC3 => Event::Refresh,
}
}
}
fn set_refresh_rate(&mut self, fps: u32) {
if fps == 0 {
self.window.timeout(-1);
} else {
self.window.timeout(1000 / fps as i32);
}
}
}

View File

@ -1,11 +1,13 @@
use event;
use theme;
// Module is not named `ncurses` to avoir naming conflict
#[cfg(feature = "ncurses")]
pub mod curses;
#[cfg(feature = "termion")]
pub mod termion;
#[cfg(feature = "cursive_termion")]
mod termion;
mod curses;
pub use self::curses::*;
#[cfg(feature = "cursive_termion")]
pub use self::termion::*;
pub trait Backend {
fn init() -> Self;

View File

@ -56,11 +56,12 @@
//! and log to it instead of stdout.
#![deny(missing_docs)]
#[cfg(feature = "ncurses")]
#[cfg(feature = "cursive_ncurses")]
extern crate ncurses;
#[cfg(feature = "termion")]
#[cfg(feature = "cursive_pancurses")]
extern crate pancurses;
#[cfg(feature = "cursive_termion")]
extern crate termion;
extern crate toml;
extern crate unicode_segmentation;
extern crate unicode_width;
@ -109,7 +110,7 @@ mod utf8;
mod backend;
use backend::{Backend};
use backend::Backend;
use event::{Callback, Event, EventResult};
@ -156,18 +157,18 @@ pub struct Cursive {
new_default!(Cursive);
// Use the Ncurses backend.
// TODO: make this feature-driven
#[cfg(feature = "termion")]
#[doc(hidden)]
pub type B = backend::termion::TermionBackend;
#[cfg(feature = "cursive_ncurses")]
pub type B = backend::NcursesBackend;
#[doc(hidden)]
#[cfg(feature = "ncurses")]
pub type B = backend::curses::NcursesBackend;
#[cfg(feature = "cursive_pancurses")]
pub type B = backend::PancursesBackend;
#[doc(hidden)]
#[cfg(feature = "cursive_termion")]
pub type B = backend::TermionBackend;
impl Cursive {
/// Creates a new Cursive root, and initialize ncurses.
/// Creates a new Cursive root, and initialize the back-end.
pub fn new() -> Self {
// Default delay is way too long. 25 is imperceptible yet works fine.
let mut backend = B::init();