@ -4,19 +4,23 @@ use std::{
} ;
use crossterm ::{
execute , queue , terminal , Clear , ClearType , Crossterm , ErrorKind , Goto , Hide , SetAttr , SetBg ,
SetFg , Show ,
cursor ::{ Hide , MoveTo , Show } ,
execute , queue ,
screen ::AlternateScreen ,
style ::{
Attribute as CAttribute , Color as CColor , SetAttribute , SetBackgroundColor ,
SetForegroundColor ,
} ,
terminal ::{ self , Clear , ClearType } ,
Output ,
} ;
use crate ::{
backend ::Backend ,
buffer ::Cell ,
layout ::Rect ,
style ::{ Color , Modifier , Style } ,
} ;
use crate ::backend ::Backend ;
use crate ::style ::{ Color , Modifier } ;
use crate ::{ buffer ::Cell , layout ::Rect , style } ;
pub struct CrosstermBackend < W : Write > {
alternate_screen : Option < crossterm:: AlternateScreen> ,
alternate_screen : Option < AlternateScreen > ,
stdout : W ,
}
@ -33,7 +37,7 @@ where
pub fn with_alternate_screen (
stdout : W ,
alternate_screen : crossterm:: AlternateScreen,
alternate_screen : AlternateScreen,
) -> Result < CrosstermBackend < W > , io ::Error > {
Ok ( CrosstermBackend {
alternate_screen : Some ( alternate_screen ) ,
@ -41,30 +45,12 @@ where
} )
}
pub fn alternate_screen ( & self ) -> Option < & crossterm:: AlternateScreen> {
pub fn alternate_screen ( & self ) -> Option < & AlternateScreen> {
match & self . alternate_screen {
Some ( alt_screen ) = > Some ( & alt_screen ) ,
None = > None ,
}
}
pub fn crossterm ( & self ) -> crossterm ::Crossterm {
Crossterm ::new ( )
}
}
fn convert_error ( error : ErrorKind ) -> io ::Error {
match error {
ErrorKind ::IoError ( err ) = > err ,
ErrorKind ::FmtError ( err ) = > {
io ::Error ::new ( io ::ErrorKind ::Other , format! ( "Invalid formatting: {}" , err ) )
}
ErrorKind ::ResizingTerminalFailure ( err ) = > io ::Error ::new (
io ::ErrorKind ::Other ,
format! ( "Failed to resize terminal: {}" , err ) ,
) ,
_ = > io ::Error ::new ( io ::ErrorKind ::Other , "Unknown crossterm error" ) ,
}
}
impl < W > Write for CrosstermBackend < W >
@ -84,53 +70,21 @@ impl<W> Backend for CrosstermBackend<W>
where
W : Write ,
{
fn clear ( & mut self ) -> io ::Result < ( ) > {
execute ! ( self . stdout , Clear ( ClearType ::All ) ) . map_err ( convert_error )
}
fn hide_cursor ( & mut self ) -> io ::Result < ( ) > {
execute ! ( self . stdout , Hide ) . map_err ( convert_error )
}
fn show_cursor ( & mut self ) -> io ::Result < ( ) > {
execute ! ( self . stdout , Show ) . map_err ( convert_error )
}
fn get_cursor ( & mut self ) -> io ::Result < ( u16 , u16 ) > {
let cursor = crossterm ::cursor ( ) ;
Ok ( cursor . pos ( ) )
}
fn set_cursor ( & mut self , x : u16 , y : u16 ) -> io ::Result < ( ) > {
execute ! ( self . stdout , Goto ( x , y ) ) . map_err ( convert_error )
}
fn size ( & self ) -> io ::Result < Rect > {
let terminal = terminal ( ) ;
let ( width , height ) = terminal . terminal_size ( ) ;
// crossterm reports max 0-based col/row index instead of count
Ok ( Rect ::new ( 0 , 0 , width , height ) )
}
fn flush ( & mut self ) -> io ::Result < ( ) > {
self . stdout . flush ( )
}
fn draw < ' a , I > ( & mut self , content : I ) -> io ::Result < ( ) >
where
I : Iterator < Item = ( u16 , u16 , & ' a Cell ) > ,
{
use std:: fmt::Write ;
use fmt ::Write ;
let mut string = String ::with_capacity ( content . size_hint ( ) . 0 * 3 ) ;
let mut style = Style::default ( ) ;
let mut style = style ::Style ::default ( ) ;
let mut last_y = 0 ;
let mut last_x = 0 ;
let mut inst = 0 ;
for ( x , y , cell ) in content {
if y ! = last_y | | x ! = last_x + 1 | | inst = = 0 {
queue! ( string , Goto( x , y ) ) ? ;
map_error ( queue ! ( string , MoveTo ( x , y ) ) ) ? ;
}
last_x = x ;
last_y = y ;
@ -144,14 +98,14 @@ where
style . modifier = cell . style . modifier ;
}
if cell . style . fg ! = style . fg {
let color = to_crossterm_color ( cell . style . fg ) ;
queue! ( string , SetF g( color ) ) ? ;
let color = CColor::from ( cell . style . fg ) ;
map_error( queue! ( string , SetF ore groundColor ( color ) ) ) ? ;
style . fg = cell . style . fg ;
inst + = 1 ;
}
if cell . style . bg ! = style . bg {
let color = to_crossterm_color ( cell . style . bg ) ;
queue! ( string , SetB g( color ) ) ? ;
let color = CColor::from ( cell . style . bg ) ;
map_error( queue! ( string , SetB ack groundColor ( color ) ) ) ? ;
style . bg = cell . style . bg ;
inst + = 1 ;
}
@ -160,18 +114,75 @@ where
inst + = 1 ;
}
write !(
map_error ( queue !(
self . stdout ,
"{}{}{}{}" ,
string ,
Set Fg( crossterm :: Color::Reset ) ,
Set Bg( crossterm ::Color ::Reset ) ,
SetAttr ( crossterm ::Attribute ::Reset )
) ? ;
Output ( string ) ,
SetForegroundColor( CColor ::Reset ) ,
Set BackgroundColor( C Color::Reset ) ,
Set Attribute( CAttribute ::Reset )
) )
}
Crossterm ::new ( ) . color ( ) . reset ( ) ? ;
fn hide_cursor ( & mut self ) -> io ::Result < ( ) > {
map_error ( execute ! ( self . stdout , Hide ) )
}
Ok ( ( ) )
fn show_cursor ( & mut self ) -> io ::Result < ( ) > {
map_error ( execute ! ( self . stdout , Show ) )
}
fn get_cursor ( & mut self ) -> io ::Result < ( u16 , u16 ) > {
crossterm ::cursor ::position ( )
. map_err ( | e | io ::Error ::new ( io ::ErrorKind ::Other , e . to_string ( ) ) )
}
fn set_cursor ( & mut self , x : u16 , y : u16 ) -> io ::Result < ( ) > {
map_error ( execute ! ( self . stdout , MoveTo ( x , y ) ) )
}
fn clear ( & mut self ) -> io ::Result < ( ) > {
map_error ( execute ! ( self . stdout , Clear ( ClearType ::All ) ) )
}
fn size ( & self ) -> io ::Result < Rect > {
let ( width , height ) =
terminal ::size ( ) . map_err ( | e | io ::Error ::new ( io ::ErrorKind ::Other , e . to_string ( ) ) ) ? ;
Ok ( Rect ::new ( 0 , 0 , width , height ) )
}
fn flush ( & mut self ) -> io ::Result < ( ) > {
< CrosstermBackend < W > as Write > ::flush ( self )
}
}
fn map_error ( error : crossterm ::Result < ( ) > ) -> io ::Result < ( ) > {
error . map_err ( | e | io ::Error ::new ( io ::ErrorKind ::Other , e . to_string ( ) ) )
}
impl From < Color > for CColor {
fn from ( color : Color ) -> Self {
match color {
Color ::Reset = > CColor ::Reset ,
Color ::Black = > CColor ::Black ,
Color ::Red = > CColor ::DarkRed ,
Color ::Green = > CColor ::DarkGreen ,
Color ::Yellow = > CColor ::DarkYellow ,
Color ::Blue = > CColor ::DarkBlue ,
Color ::Magenta = > CColor ::DarkMagenta ,
Color ::Cyan = > CColor ::DarkCyan ,
Color ::Gray = > CColor ::Grey ,
Color ::DarkGray = > CColor ::DarkGrey ,
Color ::LightRed = > CColor ::Red ,
Color ::LightGreen = > CColor ::Green ,
Color ::LightBlue = > CColor ::Blue ,
Color ::LightYellow = > CColor ::Yellow ,
Color ::LightMagenta = > CColor ::Magenta ,
Color ::LightCyan = > CColor ::Cyan ,
Color ::White = > CColor ::White ,
Color ::Indexed ( i ) = > CColor ::AnsiValue ( i ) ,
Color ::Rgb ( r , g , b ) = > CColor ::Rgb { r , g , b } ,
}
}
}
@ -187,57 +198,57 @@ impl ModifierDiff {
where
W : fmt ::Write ,
{
use crossterm ::Attribute ;
//use crossterm::Attribute;
let removed = self . from - self . to ;
if removed . contains ( Modifier ::REVERSED ) {
queue! ( w , SetAttr ( Attribute ::NoInverse ) ) ? ;
map_error( queue! ( w , SetAttr ibute( CAttribute ::NoReverse ) ) ) ? ;
}
if removed . contains ( Modifier ::BOLD ) {
queue! ( w , SetAttr ( Attribute::NormalIntensity ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NormalIntensity ) ) ) ? ;
if self . to . contains ( Modifier ::DIM ) {
queue! ( w , SetAttr ( Attribute::Dim ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Dim ) ) ) ? ;
}
}
if removed . contains ( Modifier ::ITALIC ) {
queue! ( w , SetAttr ( Attribute::NoItalic ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NoItalic ) ) ) ? ;
}
if removed . contains ( Modifier ::UNDERLINED ) {
queue! ( w , SetAttr ( Attribute::NoUnderline ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NoUnderline ) ) ) ? ;
}
if removed . contains ( Modifier ::DIM ) {
queue! ( w , SetAttr ( Attribute::NormalIntensity ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NormalIntensity ) ) ) ? ;
}
if removed . contains ( Modifier ::CROSSED_OUT ) {
queue! ( w , SetAttr ( Attribute::NotCrossedOut ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NotCrossedOut ) ) ) ? ;
}
if removed . contains ( Modifier ::SLOW_BLINK ) | | removed . contains ( Modifier ::RAPID_BLINK ) {
queue! ( w , SetAttr ( Attribute::NoBlink ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NoBlink ) ) ) ? ;
}
let added = self . to - self . from ;
if added . contains ( Modifier ::REVERSED ) {
queue! ( w , SetAttr ( Attribute::Reverse ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Reverse ) ) ) ? ;
}
if added . contains ( Modifier ::BOLD ) {
queue! ( w , SetAttr ( Attribute::Bold ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Bold ) ) ) ? ;
}
if added . contains ( Modifier ::ITALIC ) {
queue! ( w , SetAttr ( Attribute::Italic ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Italic ) ) ) ? ;
}
if added . contains ( Modifier ::UNDERLINED ) {
queue! ( w , SetAttr ( Attribute::Underlined ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Underlined ) ) ) ? ;
}
if added . contains ( Modifier ::DIM ) {
queue! ( w , SetAttr ( Attribute::Dim ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Dim ) ) ) ? ;
}
if added . contains ( Modifier ::CROSSED_OUT ) {
queue! ( w , SetAttr ( Attribute::CrossedOut ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::CrossedOut ) ) ) ? ;
}
if added . contains ( Modifier ::SLOW_BLINK ) {
queue! ( w , SetAttr ( Attribute::SlowBlink ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::SlowBlink ) ) ) ? ;
}
if added . contains ( Modifier ::RAPID_BLINK ) {
queue! ( w , SetAttr ( Attribute::RapidBlink ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::RapidBlink ) ) ) ? ;
}
Ok ( ( ) )
@ -250,47 +261,21 @@ impl ModifierDiff {
where
W : fmt ::Write ,
{
use crossterm ::Attribute ;
let removed = self . from - self . to ;
if removed . contains ( Modifier ::BOLD ) {
queue! ( w , SetAttr ( Attribute::NormalIntensity ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NormalIntensity ) ) ) ? ;
}
if removed . contains ( Modifier ::UNDERLINED ) {
queue! ( w , SetAttr ( Attribute::NoUnderline ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::NoUnderline ) ) ) ? ;
}
let added = self . to - self . from ;
if added . contains ( Modifier ::BOLD ) {
queue! ( w , SetAttr ( Attribute::Bold ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Bold ) ) ) ? ;
}
if added . contains ( Modifier ::UNDERLINED ) {
queue! ( w , SetAttr ( Attribute::Underlined ) ) ? ;
map_error( queue! ( w , SetAttr ibute ( C Attribute::Underlined ) ) ) ? ;
}
Ok ( ( ) )
}
}
fn to_crossterm_color ( color : Color ) -> crossterm ::Color {
match color {
Color ::Reset = > crossterm ::Color ::Reset ,
Color ::Black = > crossterm ::Color ::Black ,
Color ::Red = > crossterm ::Color ::DarkRed ,
Color ::Green = > crossterm ::Color ::DarkGreen ,
Color ::Yellow = > crossterm ::Color ::DarkYellow ,
Color ::Blue = > crossterm ::Color ::DarkBlue ,
Color ::Magenta = > crossterm ::Color ::DarkMagenta ,
Color ::Cyan = > crossterm ::Color ::DarkCyan ,
Color ::Gray = > crossterm ::Color ::Grey ,
Color ::DarkGray = > crossterm ::Color ::DarkGrey ,
Color ::LightRed = > crossterm ::Color ::Red ,
Color ::LightGreen = > crossterm ::Color ::Green ,
Color ::LightBlue = > crossterm ::Color ::Blue ,
Color ::LightYellow = > crossterm ::Color ::Yellow ,
Color ::LightMagenta = > crossterm ::Color ::Magenta ,
Color ::LightCyan = > crossterm ::Color ::Cyan ,
Color ::White = > crossterm ::Color ::White ,
Color ::Indexed ( i ) = > crossterm ::Color ::AnsiValue ( i ) ,
Color ::Rgb ( r , g , b ) = > crossterm ::Color ::Rgb { r , g , b } ,
}
}