mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 19:26:09 +00:00
Mostly add pancurses support. Test pancurses support on Travis
This commit is contained in:
parent
9871e12734
commit
01951aa213
@ -3,3 +3,9 @@ cache: cargo
|
|||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- nightly
|
- nightly
|
||||||
|
script:
|
||||||
|
- cargo build --verbose
|
||||||
|
- cargo test --verbose
|
||||||
|
-
|
||||||
|
- cargo build --verbose --features=pancurses --no-default-features
|
||||||
|
- cargo test --verbose --features=pancurses --no-default-features
|
||||||
|
16
Cargo.toml
16
Cargo.toml
@ -14,6 +14,12 @@ version = "0.3.1"
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
skeptic = "0.6"
|
skeptic = "0.6"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
cursive_ncurses = ["ncurses"]
|
||||||
|
cursive_pancurses = ["pancurses"]
|
||||||
|
cursive_termion = ["termion"]
|
||||||
|
default = ["cursive_ncurses"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
odds = "0.2"
|
odds = "0.2"
|
||||||
toml = "0.2"
|
toml = "0.2"
|
||||||
@ -21,20 +27,22 @@ unicode-segmentation = "0.1"
|
|||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
|
|
||||||
[dependencies.ncurses]
|
[dependencies.ncurses]
|
||||||
|
version = "5.83.0"
|
||||||
features = ["wide"]
|
features = ["wide"]
|
||||||
optional = true
|
optional = true
|
||||||
version = "5.83.0"
|
version = "5.83.0"
|
||||||
|
|
||||||
[dependencies.termion]
|
[dependencies.pancurses]
|
||||||
|
version = "0.3.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.termion]
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
skeptic = "0.6"
|
skeptic = "0.6"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["ncurses"]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "cursive"
|
name = "cursive"
|
||||||
|
40
src/backend/curses/mod.rs
Normal file
40
src/backend/curses/mod.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,6 @@ impl backend::Backend for NcursesBackend {
|
|||||||
|
|
||||||
/// Returns the Key enum corresponding to the given ncurses event.
|
/// Returns the Key enum corresponding to the given ncurses event.
|
||||||
fn parse_ncurses_char(ch: i32) -> Event {
|
fn parse_ncurses_char(ch: i32) -> Event {
|
||||||
|
|
||||||
match ch {
|
match ch {
|
||||||
// Value sent by ncurses when nothing happens
|
// Value sent by ncurses when nothing happens
|
||||||
-1 => Event::Refresh,
|
-1 => Event::Refresh,
|
221
src/backend/curses/pan.rs
Normal file
221
src/backend/curses/pan.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
use event;
|
use event;
|
||||||
use theme;
|
use theme;
|
||||||
|
|
||||||
// Module is not named `ncurses` to avoir naming conflict
|
#[cfg(feature = "cursive_termion")]
|
||||||
#[cfg(feature = "ncurses")]
|
mod termion;
|
||||||
pub mod curses;
|
mod curses;
|
||||||
#[cfg(feature = "termion")]
|
|
||||||
pub mod termion;
|
pub use self::curses::*;
|
||||||
|
#[cfg(feature = "cursive_termion")]
|
||||||
|
pub use self::termion::*;
|
||||||
|
|
||||||
pub trait Backend {
|
pub trait Backend {
|
||||||
fn init() -> Self;
|
fn init() -> Self;
|
||||||
|
25
src/lib.rs
25
src/lib.rs
@ -56,11 +56,12 @@
|
|||||||
//! and log to it instead of stdout.
|
//! and log to it instead of stdout.
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
#[cfg(feature = "ncurses")]
|
#[cfg(feature = "cursive_ncurses")]
|
||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
#[cfg(feature = "termion")]
|
#[cfg(feature = "cursive_pancurses")]
|
||||||
|
extern crate pancurses;
|
||||||
|
#[cfg(feature = "cursive_termion")]
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
|
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
extern crate unicode_segmentation;
|
extern crate unicode_segmentation;
|
||||||
extern crate unicode_width;
|
extern crate unicode_width;
|
||||||
@ -109,7 +110,7 @@ mod utf8;
|
|||||||
mod backend;
|
mod backend;
|
||||||
|
|
||||||
|
|
||||||
use backend::{Backend};
|
use backend::Backend;
|
||||||
|
|
||||||
use event::{Callback, Event, EventResult};
|
use event::{Callback, Event, EventResult};
|
||||||
|
|
||||||
@ -156,18 +157,18 @@ pub struct Cursive {
|
|||||||
|
|
||||||
new_default!(Cursive);
|
new_default!(Cursive);
|
||||||
|
|
||||||
// Use the Ncurses backend.
|
|
||||||
// TODO: make this feature-driven
|
|
||||||
#[cfg(feature = "termion")]
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub type B = backend::termion::TermionBackend;
|
#[cfg(feature = "cursive_ncurses")]
|
||||||
|
pub type B = backend::NcursesBackend;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "ncurses")]
|
#[cfg(feature = "cursive_pancurses")]
|
||||||
pub type B = backend::curses::NcursesBackend;
|
pub type B = backend::PancursesBackend;
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(feature = "cursive_termion")]
|
||||||
|
pub type B = backend::TermionBackend;
|
||||||
|
|
||||||
impl Cursive {
|
impl Cursive {
|
||||||
/// Creates a new Cursive root, and initialize ncurses.
|
/// Creates a new Cursive root, and initialize the back-end.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Default delay is way too long. 25 is imperceptible yet works fine.
|
// Default delay is way too long. 25 is imperceptible yet works fine.
|
||||||
let mut backend = B::init();
|
let mut backend = B::init();
|
||||||
|
Loading…
Reference in New Issue
Block a user