Merge pull request #70 from nabijaczleweli/master

Pancurses back-end
This commit is contained in:
Alexandre Bury 2016-10-11 10:52:18 -07:00 committed by GitHub
commit 01f4689822
14 changed files with 319 additions and 59 deletions

View File

@ -3,3 +3,12 @@ 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
-
- cargo build --verbose --features=termion --no-default-features
- cargo test --verbose --features=termion --no-default-features

View File

@ -21,13 +21,17 @@ unicode-segmentation = "0.1"
unicode-width = "0.1"
[dependencies.ncurses]
version = "5.83.0"
features = ["wide"]
optional = true
version = "5.83.0"
[dependencies.pancurses]
version = "0.4.0"
optional = true
[dependencies.termion]
optional = true
version = "1.1.1"
optional = true
[dev-dependencies]
rand = "0.3"

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

@ -0,0 +1,40 @@
use theme::{BaseColor, Color};
#[cfg(feature = "ncurses")]
mod n;
#[cfg(feature = "ncurses")]
pub use self::n::*;
#[cfg(feature = "pancurses")]
mod pan;
#[cfg(feature = "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

@ -1,13 +1,14 @@
extern crate ncurses;
use backend;
use event::{Event, Key};
use ncurses;
use theme::{BaseColor, Color, ColorStyle, Effect};
use utf8;
pub struct NcursesBackend;
pub struct Concrete;
impl backend::Backend for NcursesBackend {
impl backend::Backend for Concrete {
fn init() -> Self {
::std::env::set_var("ESCDELAY", "25");
ncurses::setlocale(ncurses::LcCategory::all, "");
@ -20,7 +21,7 @@ impl backend::Backend for NcursesBackend {
ncurses::wbkgd(unsafe { ncurses::stdscr() },
ncurses::COLOR_PAIR(ColorStyle::Background.id()));
NcursesBackend
Concrete
}
fn screen_size(&self) -> (usize, usize) {
@ -105,7 +106,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,

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

@ -0,0 +1,215 @@
extern crate pancurses;
use backend;
use event::{Event, Key};
use self::super::find_closest;
use theme::{Color, ColorStyle, Effect};
pub struct Concrete {
window: pancurses::Window,
}
impl backend::Backend for Concrete {
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));
Concrete { 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(&mut 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,
pancurses::Input::KeyBreak => Event::Key(Key::PauseBreak),
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),
pancurses::Input::KeyF0 => Event::Key(Key::F0),
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),
pancurses::Input::KeyF13 => Event::Shift(Key::F1),
pancurses::Input::KeyF14 => Event::Shift(Key::F2),
pancurses::Input::KeyF15 => Event::Shift(Key::F3),
pancurses::Input::KeyDL => Event::Refresh,
pancurses::Input::KeyIL => Event::Key(Key::Ins),
pancurses::Input::KeyDC => Event::Key(Key::Del),
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::Key(Key::PageDown),
pancurses::Input::KeyPPage => Event::Key(Key::PageUp),
pancurses::Input::KeySTab => Event::Shift(Key::Tab),
pancurses::Input::KeyCTab => Event::Ctrl(Key::Tab),
pancurses::Input::KeyCATab => Event::CtrlAlt(Key::Tab),
pancurses::Input::KeyEnter => Event::Key(Key::Enter),
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::Shift(Key::Tab),
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::Shift(Key::Del),
pancurses::Input::KeySDL => Event::Refresh,
pancurses::Input::KeySelect => Event::Refresh,
pancurses::Input::KeySEnd => Event::Shift(Key::End),
pancurses::Input::KeySEOL => Event::Refresh,
pancurses::Input::KeySExit => Event::Refresh,
pancurses::Input::KeySFind => Event::Refresh,
pancurses::Input::KeySHome => Event::Shift(Key::Home),
pancurses::Input::KeySIC => Event::Shift(Key::Ins),
pancurses::Input::KeySLeft => Event::Refresh,
pancurses::Input::KeySMessage => Event::Refresh,
pancurses::Input::KeySMove => Event::Refresh,
pancurses::Input::KeySNext => Event::Shift(Key::PageDown),
pancurses::Input::KeySOptions => Event::Refresh,
pancurses::Input::KeySPrevious => Event::Shift(Key::PageUp),
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: mouse support
pancurses::Input::KeyMouse => Event::Refresh,
pancurses::Input::KeyA1 => Event::Refresh,
pancurses::Input::KeyA3 => Event::Refresh,
pancurses::Input::KeyB2 => Event::Key(Key::NumpadCenter),
pancurses::Input::KeyC1 => Event::Refresh,
pancurses::Input::KeyC3 => Event::Refresh,
}
} else {
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;
mod termion;
mod curses;
pub use self::curses::*;
#[cfg(feature = "termion")]
pub use self::termion::*;
pub trait Backend {
fn init() -> Self;

View File

@ -1,20 +1,21 @@
extern crate termion;
use ::backend;
use ::event::{Event, Key};
use std::io::Write;
use termion;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use self::termion::input::TermRead;
use self::termion::raw::IntoRawMode;
use ::theme::{BaseColor, Color, ColorStyle, Effect};
pub struct TermionBackend {
pub struct Concrete {
terminal: termion::raw::RawTerminal<::std::io::Stdout>,
}
impl backend::Backend for TermionBackend {
impl backend::Backend for Concrete {
fn init() -> Self {
print!("{}", termion::cursor::Hide);
let backend = TermionBackend {
let backend = Concrete {
terminal: ::std::io::stdout().into_raw_mode().unwrap(),
};

View File

@ -114,9 +114,14 @@ pub enum Key {
/// Page Down key
PageDown,
/// Pause Break key
PauseBreak,
/// The 5 in the center of the keypad, when numlock is disabled.
NumpadCenter,
/// F0 key
F0,
/// F1 key
F1,
/// F2 key
@ -153,6 +158,7 @@ impl Key {
/// If `n == 0 || n > 12`
pub fn from_f(n: u8) -> Key {
match n {
0 => Key::F0,
1 => Key::F1,
2 => Key::F2,
3 => Key::F3,

View File

@ -56,11 +56,6 @@
//! and log to it instead of stdout.
#![deny(missing_docs)]
#[cfg(feature = "ncurses")]
extern crate ncurses;
#[cfg(feature = "termion")]
extern crate termion;
extern crate toml;
extern crate unicode_segmentation;
extern crate unicode_width;
@ -106,10 +101,11 @@ mod with;
mod div;
mod utf8;
mod backend;
#[doc(hidden)]
pub mod backend;
use backend::{Backend};
use backend::Backend;
use event::{Callback, Event, EventResult};
@ -148,7 +144,7 @@ pub struct Cursive {
running: bool,
backend: B,
backend: backend::Concrete,
cb_source: mpsc::Receiver<Box<Fn(&mut Cursive) + Send>>,
cb_sink: mpsc::Sender<Box<Fn(&mut Cursive) + Send>>,
@ -156,21 +152,11 @@ 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;
#[doc(hidden)]
#[cfg(feature = "ncurses")]
pub type B = backend::curses::NcursesBackend;
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();
let mut backend = backend::Concrete::init();
let theme = theme::load_default();
theme.activate(&mut backend);
@ -225,7 +211,7 @@ impl Cursive {
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// #
/// # use cursive::{Cursive, event};
@ -362,7 +348,7 @@ impl Cursive {
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::{Cursive, views, view};
/// # use cursive::traits::*;
@ -388,7 +374,7 @@ impl Cursive {
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::{Cursive, views};
/// # use cursive::traits::*;
@ -415,7 +401,7 @@ impl Cursive {
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::*;
/// # fn main() {
@ -434,7 +420,7 @@ impl Cursive {
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::*;
/// # fn main() {

View File

@ -1,8 +1,7 @@
//! Makes drawing on ncurses windows easier.
use B;
use backend::Backend;
use backend::{self, Backend};
use std::cell::Cell;
use std::cmp::min;
use std::rc::Rc;
@ -27,7 +26,7 @@ pub struct Printer<'a> {
/// `true` if nothing has been drawn yet.
new: Rc<Cell<bool>>,
/// Backend used to actually draw things
backend: &'a B,
backend: &'a backend::Concrete,
}
impl<'a> Printer<'a> {
@ -35,7 +34,7 @@ impl<'a> Printer<'a> {
///
/// But nobody needs to know that.
#[doc(hidden)]
pub fn new<T: Into<Vec2>>(size: T, theme: Theme, backend: &'a B) -> Self {
pub fn new<T: Into<Vec2>>(size: T, theme: Theme, backend: &'a backend::Concrete) -> Self {
Printer {
offset: Vec2::zero(),
size: size.into(),
@ -118,8 +117,8 @@ impl<'a> Printer<'a> {
/// ```no_run
/// # use cursive::Printer;
/// # use cursive::theme;
/// # use cursive::B;
/// # let b = B{};
/// # use cursive::backend::{self, Backend};
/// # let b = backend::Concrete::init();
/// # let printer = Printer::new((6,4), theme::load_default(), &b);
/// printer.with_color(theme::ColorStyle::Highlight, |printer| {
/// printer.print((0,0), "This text is highlighted!");
@ -151,8 +150,8 @@ impl<'a> Printer<'a> {
/// ```no_run
/// # use cursive::Printer;
/// # use cursive::theme;
/// # use cursive::B;
/// # let b = B{};
/// # use cursive::backend::{self, Backend};
/// # let b = backend::Concrete::init();
/// # let printer = Printer::new((6,4), theme::load_default(), &b);
/// printer.print_box((0,0), (6,4), false);
/// ```

View File

@ -115,9 +115,7 @@
//! ```
use B;
use backend::Backend;
use backend::{self, Backend};
use std::fs::File;
use std::io;
use std::io::Read;
@ -230,7 +228,7 @@ impl Theme {
/// **Don't use this directly.** Uses [`Cursive::set_theme`] instead.
///
/// [`Cursive::set_theme`]: ../struct.Cursive.html#method.set_theme
pub fn activate(&self, backend: &mut B) {
pub fn activate(&self, backend: &mut backend::Concrete) {
// Initialize each color with the backend
backend.init_color_style(ColorStyle::Background,
&self.colors.view,

View File

@ -130,13 +130,13 @@ impl ScrollBase {
///
/// # Examples
///
/// ```
/// ```no_run
/// # use cursive::view::ScrollBase;
/// # use cursive::Printer;
/// # use cursive::theme;
/// # use cursive::B;
/// # use cursive::backend::{self, Backend};
/// # let scrollbase = ScrollBase::new();
/// # let b = B{};
/// # let b = backend::Concrete::init();
/// # let printer = Printer::new((5,1), theme::load_default(), &b);
/// # let printer = &printer;
/// let lines = ["Line 1", "Line number 2"];

View File

@ -21,7 +21,7 @@ use view::View;
///
/// [edit example]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::Cursive;
/// # use cursive::traits::*;

View File

@ -24,7 +24,7 @@ use views::MenuPopup;
///
/// # Examples
///
/// ```
/// ```no_run
/// # extern crate cursive;
/// # use cursive::Cursive;
/// # use cursive::views::{SelectView, Dialog, TextView};