use std::fmt;
use std::io;
use std::sync::{LockResult, TryLockResult};
use std::time::Duration;
use crate::priv_util::{map_lock_result, map_try_lock_result};
use crate::signal::{Signal, SignalSet};
use crate::sys;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Color {
Black,
Blue,
Cyan,
Green,
Magenta,
Red,
White,
Yellow,
}
bitflags!{
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct Style: u8 {
const BOLD = 1 << 0;
const ITALIC = 1 << 1;
const REVERSE = 1 << 2;
const UNDERLINE = 1 << 3;
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Theme {
pub fg: Option<Color>,
pub bg: Option<Color>,
pub style: Style,
}
impl Theme {
pub fn new<F,B,S>(fg: F, bg: B, style: S) -> Theme
where
F: Into<Option<Color>>,
B: Into<Option<Color>>,
S: Into<Option<Style>> {
Theme {
fg: fg.into(),
bg: bg.into(),
style: style.into().unwrap_or_default(),
}
}
pub fn fg<F>(mut self, fg: F) -> Theme
where F: Into<Option<Color>> {
self.fg = fg.into();
self
}
pub fn bg<B>(mut self, bg: B) -> Theme
where B: Into<Option<Color>> {
self.bg = bg.into();
self
}
pub fn style<S>(mut self, style: S) -> Theme
where S: Into<Option<Style>> {
self.style = style.into().unwrap_or_default();
self
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Cursor {
pub line: usize,
pub column: usize,
}
impl Cursor {
#[inline]
pub fn next(&self, size: Size) -> Option<Cursor> {
let mut line = self.line;
let mut column = self.column + 1;
if column >= size.columns {
column = 0;
line += 1;
}
if line >= size.lines {
None
} else {
Some(Cursor{line, column})
}
}
#[inline]
pub fn previous(&self, size: Size) -> Option<Cursor> {
if self.column == 0 {
if self.line == 0 {
None
} else {
Some(Cursor{line: self.line - 1, column: size.columns - 1})
}
} else {
Some(Cursor{line: self.line, column: self.column - 1})
}
}
#[inline]
pub fn first() -> Cursor {
Cursor{
line: 0,
column: 0,
}
}
#[inline]
pub fn last(size: Size) -> Cursor {
Cursor{
line: size.lines - 1,
column: size.columns - 1,
}
}
#[inline]
pub fn is_out_of_bounds(&self, size: Size) -> bool {
self.line >= size.lines || self.column >= size.columns
}
pub(crate) fn as_index(&self, size: Size) -> usize {
self.line * size.columns + self.column
}
}
impl From<(usize, usize)> for Cursor {
fn from((line, column): (usize, usize)) -> Cursor {
Cursor{line, column}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CursorMode {
Normal,
Invisible,
Overwrite,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Event {
Key(Key),
Mouse(MouseEvent),
Raw(usize),
Resize(Size),
Signal(Signal),
NoEvent,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Key {
Backspace,
Enter,
Escape,
Tab,
Up,
Down,
Left,
Right,
Delete,
Insert,
Home,
End,
PageUp,
PageDown,
Char(char),
Ctrl(char),
F(u32),
}
impl From<char> for Key {
fn from(ch: char) -> Key {
use crate::util::{is_ctrl, unctrl_lower};
match ch {
'\x1b' => Key::Escape,
'\x7f' => Key::Backspace,
'\r' | '\n' => Key::Enter,
'\t' => Key::Tab,
_ if is_ctrl(ch) => Key::Ctrl(unctrl_lower(ch)),
_ => Key::Char(ch),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct MouseEvent {
pub position: Cursor,
pub input: MouseInput,
pub modifiers: ModifierState,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MouseInput {
Motion,
ButtonPressed(MouseButton),
ButtonReleased(MouseButton),
WheelUp,
WheelDown,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MouseButton {
Left,
Right,
Middle,
Other(u32),
}
bitflags!{
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ModifierState: u8 {
const ALT = 1 << 0;
const CTRL = 1 << 1;
const SHIFT = 1 << 2;
}
}
#[derive(Copy, Clone, Debug)]
pub struct PrepareConfig {
pub block_signals: bool,
pub enable_control_flow: bool,
pub enable_keypad: bool,
pub enable_mouse: bool,
pub always_track_motion: bool,
pub report_signals: SignalSet,
}
impl Default for PrepareConfig {
fn default() -> PrepareConfig {
PrepareConfig{
block_signals: true,
enable_control_flow: false,
enable_keypad: true,
enable_mouse: false,
always_track_motion: false,
report_signals: SignalSet::new(),
}
}
}
#[must_use = "the result of `terminal.prepare()` should be passed to \
`terminal.restore()` to restore terminal to its original state"]
pub struct PrepareState(sys::PrepareState);
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Size {
pub lines: usize,
pub columns: usize,
}
impl Size {
#[inline]
pub fn area(&self) -> usize {
self.checked_area().unwrap_or_else(
|| panic!("overflow in Size::area {:?}", self))
}
#[inline]
pub fn checked_area(&self) -> Option<usize> {
self.lines.checked_mul(self.columns)
}
}
pub struct Terminal(pub(crate) sys::Terminal);
pub struct TerminalReadGuard<'a>(sys::TerminalReadGuard<'a>);
pub struct TerminalWriteGuard<'a>(sys::TerminalWriteGuard<'a>);
impl Terminal {
pub fn new() -> io::Result<Terminal> {
Ok(Terminal(sys::Terminal::stdout()?))
}
pub fn stderr() -> io::Result<Terminal> {
Ok(Terminal(sys::Terminal::stderr()?))
}
#[inline]
pub fn name(&self) -> &str {
self.0.name()
}
#[inline]
pub fn lock_read(&self) -> LockResult<TerminalReadGuard> {
map_lock_result(self.0.lock_read(), TerminalReadGuard)
}
#[inline]
pub fn lock_write(&self) -> LockResult<TerminalWriteGuard> {
map_lock_result(self.0.lock_write(), TerminalWriteGuard)
}
#[inline]
pub fn try_lock_read(&self) -> TryLockResult<TerminalReadGuard> {
map_try_lock_result(self.0.try_lock_read(), TerminalReadGuard)
}
#[inline]
pub fn try_lock_write(&self) -> TryLockResult<TerminalWriteGuard> {
map_try_lock_result(self.0.try_lock_write(), TerminalWriteGuard)
}
}
impl Terminal {
pub fn prepare(&self, config: PrepareConfig) -> io::Result<PrepareState> {
self.0.prepare(config).map(PrepareState)
}
pub fn restore(&self, state: PrepareState) -> io::Result<()> {
self.0.restore(state.0)
}
}
impl Terminal {
pub fn wait_event(&self, timeout: Option<Duration>) -> io::Result<bool> {
self.0.wait_event(timeout)
}
pub fn read_event(&self, timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_event(timeout)
}
}
impl Terminal {
#[inline]
pub fn size(&self) -> io::Result<Size> {
self.0.size()
}
pub fn clear_screen(&self) -> io::Result<()> {
self.0.clear_screen()
}
pub fn clear_to_line_end(&self) -> io::Result<()> {
self.0.clear_to_line_end()
}
pub fn clear_to_screen_end(&self) -> io::Result<()> {
self.0.clear_to_screen_end()
}
pub fn move_up(&self, n: usize) -> io::Result<()> {
self.0.move_up(n)
}
pub fn move_down(&self, n: usize) -> io::Result<()> {
self.0.move_down(n)
}
pub fn move_left(&self, n: usize) -> io::Result<()> {
self.0.move_left(n)
}
pub fn move_right(&self, n: usize) -> io::Result<()> {
self.0.move_right(n)
}
pub fn move_to_first_column(&self) -> io::Result<()> {
self.0.move_to_first_column()
}
pub fn set_cursor_mode(&self, mode: CursorMode) -> io::Result<()> {
self.0.set_cursor_mode(mode)
}
pub fn add_style(&self, style: Style) -> io::Result<()> {
self.0.add_style(style)
}
pub fn remove_style(&self, style: Style) -> io::Result<()> {
self.0.remove_style(style)
}
pub fn set_style<S>(&self, style: S) -> io::Result<()>
where S: Into<Option<Style>> {
self.0.set_style(style.into().unwrap_or_default())
}
pub fn set_theme(&self, theme: Theme) -> io::Result<()> {
self.0.set_theme(theme)
}
pub fn set_fg<C: Into<Option<Color>>>(&self, fg: C) -> io::Result<()> {
self.0.set_fg(fg.into())
}
pub fn set_bg<C: Into<Option<Color>>>(&self, bg: C) -> io::Result<()> {
self.0.set_bg(bg.into())
}
pub fn clear_attributes(&self) -> io::Result<()> {
self.0.clear_attributes()
}
pub fn bold(&self) -> io::Result<()> {
self.add_style(Style::BOLD)
}
pub fn italic(&self) -> io::Result<()> {
self.add_style(Style::ITALIC)
}
pub fn underline(&self) -> io::Result<()> {
self.add_style(Style::UNDERLINE)
}
pub fn reverse(&self) -> io::Result<()> {
self.add_style(Style::REVERSE)
}
pub fn write_styled<F, B, S>(&self, fg: F, bg: B, style: S, s: &str)
-> io::Result<()> where
F: Into<Option<Color>>,
B: Into<Option<Color>>,
S: Into<Option<Style>>,
{
self.0.write_styled(fg.into(), bg.into(), style.into().unwrap_or_default(), s)
}
pub fn write_char(&self, ch: char) -> io::Result<()> {
self.0.write_char(ch)
}
pub fn write_str(&self, s: &str) -> io::Result<()> {
self.0.write_str(s)
}
pub fn write_fmt(&self, args: fmt::Arguments) -> io::Result<()> {
let s = args.to_string();
self.write_str(&s)
}
#[doc(hidden)]
pub fn borrow_term_write_guard(&self) -> TerminalWriteGuard {
self.lock_write().unwrap()
}
}
impl<'a> TerminalReadGuard<'a> {
pub fn prepare(&mut self, config: PrepareConfig) -> io::Result<PrepareState> {
self.0.prepare(config).map(PrepareState)
}
pub fn prepare_with_lock(&mut self, writer: &mut TerminalWriteGuard,
config: PrepareConfig) -> io::Result<PrepareState> {
self.0.prepare_with_lock(&mut writer.0, config).map(PrepareState)
}
pub fn restore(&mut self, state: PrepareState) -> io::Result<()> {
self.0.restore(state.0)
}
pub fn restore_with_lock(&mut self, writer: &mut TerminalWriteGuard,
state: PrepareState) -> io::Result<()> {
self.0.restore_with_lock(&mut writer.0, state.0)
}
pub fn wait_event(&mut self, timeout: Option<Duration>) -> io::Result<bool> {
self.0.wait_event(timeout)
}
pub fn read_event(&mut self, timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_event(timeout)
}
}
impl<'a> TerminalWriteGuard<'a> {
pub fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
#[inline]
pub fn size(&self) -> io::Result<Size> {
self.0.size()
}
pub fn clear_screen(&mut self) -> io::Result<()> {
self.0.clear_screen()
}
pub fn clear_to_line_end(&mut self) -> io::Result<()> {
self.0.clear_to_line_end()
}
pub fn clear_to_screen_end(&mut self) -> io::Result<()> {
self.0.clear_to_screen_end()
}
pub fn move_up(&mut self, n: usize) -> io::Result<()> {
self.0.move_up(n)
}
pub fn move_down(&mut self, n: usize) -> io::Result<()> {
self.0.move_down(n)
}
pub fn move_left(&mut self, n: usize) -> io::Result<()> {
self.0.move_left(n)
}
pub fn move_right(&mut self, n: usize) -> io::Result<()> {
self.0.move_right(n)
}
pub fn move_to_first_column(&mut self) -> io::Result<()> {
self.0.move_to_first_column()
}
pub fn set_cursor_mode(&mut self, mode: CursorMode) -> io::Result<()> {
self.0.set_cursor_mode(mode)
}
pub fn add_style(&mut self, style: Style) -> io::Result<()> {
self.0.add_style(style)
}
pub fn remove_style(&mut self, style: Style) -> io::Result<()> {
self.0.remove_style(style)
}
pub fn set_style<S>(&mut self, style: S) -> io::Result<()>
where S: Into<Option<Style>> {
self.0.set_style(style.into().unwrap_or_default())
}
pub fn set_theme(&mut self, theme: Theme) -> io::Result<()> {
self.0.set_theme(theme)
}
pub fn set_fg<C: Into<Option<Color>>>(&mut self, fg: C) -> io::Result<()> {
self.0.set_fg(fg.into())
}
pub fn set_bg<C: Into<Option<Color>>>(&mut self, bg: C) -> io::Result<()> {
self.0.set_bg(bg.into())
}
pub fn clear_attributes(&mut self) -> io::Result<()> {
self.0.clear_attributes()
}
pub fn bold(&mut self) -> io::Result<()> {
self.add_style(Style::BOLD)
}
pub fn italic(&mut self) -> io::Result<()> {
self.add_style(Style::ITALIC)
}
pub fn underline(&mut self) -> io::Result<()> {
self.add_style(Style::UNDERLINE)
}
pub fn reverse(&mut self) -> io::Result<()> {
self.add_style(Style::REVERSE)
}
pub fn write_styled<F, B, S>(&mut self, fg: F, bg: B, style: S, s: &str)
-> io::Result<()> where
F: Into<Option<Color>>,
B: Into<Option<Color>>,
S: Into<Option<Style>>,
{
self.0.write_styled(fg.into(), bg.into(), style.into().unwrap_or_default(), s)
}
pub fn write_char(&mut self, ch: char) -> io::Result<()> {
self.0.write_char(ch)
}
pub fn write_str(&mut self, s: &str) -> io::Result<()> {
self.0.write_str(s)
}
pub fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> {
let s = args.to_string();
self.write_str(&s)
}
#[doc(hidden)]
pub fn borrow_term_write_guard(&mut self) -> &mut Self {
self
}
}
#[cfg(unix)]
use std::path::Path;
#[cfg(unix)]
impl crate::unix::OpenTerminalExt for Terminal {
fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
sys::Terminal::open(path).map(Terminal)
}
}
#[cfg(unix)]
impl crate::unix::TerminalExt for Terminal {
fn read_raw(&mut self, buf: &mut [u8], timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw(buf, timeout)
}
}
#[cfg(unix)]
impl<'a> crate::unix::TerminalExt for TerminalReadGuard<'a> {
fn read_raw(&mut self, buf: &mut [u8], timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw(buf, timeout)
}
}
#[cfg(windows)]
impl crate::windows::TerminalExt for Terminal {
fn read_raw(&mut self, buf: &mut [u16], timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw(buf, timeout)
}
fn read_raw_event(&mut self, events: &mut [::winapi::um::wincon::INPUT_RECORD],
timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw_event(events, timeout)
}
}
#[cfg(windows)]
impl<'a> crate::windows::TerminalExt for TerminalReadGuard<'a> {
fn read_raw(&mut self, buf: &mut [u16], timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw(buf, timeout)
}
fn read_raw_event(&mut self, events: &mut [::winapi::um::wincon::INPUT_RECORD],
timeout: Option<Duration>) -> io::Result<Option<Event>> {
self.0.read_raw_event(events, timeout)
}
}