parent
e30198d9d4
commit
f923b93694
|
@ -73,7 +73,7 @@ DEPS_arena := std collections
|
||||||
DEPS_graphviz := std
|
DEPS_graphviz := std
|
||||||
DEPS_glob := std
|
DEPS_glob := std
|
||||||
DEPS_serialize := std collections log
|
DEPS_serialize := std collections log
|
||||||
DEPS_term := std collections
|
DEPS_term := std collections log
|
||||||
DEPS_semver := std
|
DEPS_semver := std
|
||||||
DEPS_uuid := std serialize rand
|
DEPS_uuid := std serialize rand
|
||||||
DEPS_sync := std
|
DEPS_sync := std
|
||||||
|
|
|
@ -8,7 +8,32 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! Simple ANSI color library
|
//! Terminal formatting library.
|
||||||
|
//!
|
||||||
|
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
|
||||||
|
//! Termina][ansi] to provide color printing, among other things. There are two implementations,
|
||||||
|
//! the `TerminfoTerminal`, which uses control characters from a
|
||||||
|
//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
|
||||||
|
//! API][win].
|
||||||
|
//!
|
||||||
|
//! ## Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate term;
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let mut t = term::stdout();
|
||||||
|
//! t.fg(term::color::GREEN);
|
||||||
|
//! println!("hello, ");
|
||||||
|
//! t.fg(term::color::RED);
|
||||||
|
//! println("world!");
|
||||||
|
//! t.reset();
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
|
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
|
||||||
|
//! [ti]: https://en.wikipedia.org/wiki/Terminfo
|
||||||
|
|
||||||
#![crate_id = "term#0.11.0-pre"]
|
#![crate_id = "term#0.11.0-pre"]
|
||||||
#![comment = "Simple ANSI color library"]
|
#![comment = "Simple ANSI color library"]
|
||||||
|
@ -19,22 +44,76 @@
|
||||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||||
html_root_url = "http://static.rust-lang.org/doc/master")]
|
html_root_url = "http://static.rust-lang.org/doc/master")]
|
||||||
|
|
||||||
#![feature(macro_rules)]
|
#![feature(macro_rules, phase)]
|
||||||
|
|
||||||
#![deny(missing_doc)]
|
#![deny(missing_doc)]
|
||||||
|
|
||||||
|
#[phase(syntax, link)] extern crate log;
|
||||||
extern crate collections;
|
extern crate collections;
|
||||||
|
|
||||||
use std::io;
|
pub use terminfo::TerminfoTerminal;
|
||||||
use std::os;
|
#[cfg(windows)]
|
||||||
use terminfo::TermInfo;
|
pub use win::WinConsole;
|
||||||
use terminfo::searcher::open;
|
|
||||||
use terminfo::parser::compiled::{parse, msys_terminfo};
|
use std::io::IoResult;
|
||||||
use terminfo::parm::{expand, Number, Variables};
|
|
||||||
|
|
||||||
pub mod terminfo;
|
pub mod terminfo;
|
||||||
|
|
||||||
// FIXME (#2807): Windows support.
|
#[cfg(windows)]
|
||||||
|
mod win;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||||
|
/// opened.
|
||||||
|
pub fn stdout() -> Option<~Terminal<~Writer:Send>:Send> {
|
||||||
|
let ti: Option<TerminfoTerminal<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stdout() as ~Writer:Send);
|
||||||
|
ti.map(|t| ~t as ~Terminal<~Writer:Send>:Send)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
|
||||||
|
/// opened.
|
||||||
|
pub fn stdout() -> Option<~Terminal<~Writer:Send>:Send> {
|
||||||
|
let ti: Option<TerminfoTerminal<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stdout() as ~Writer:Send);
|
||||||
|
|
||||||
|
match ti {
|
||||||
|
Some(t) => Some(~t as ~Terminal<~Writer:Send>:Send),
|
||||||
|
None => {
|
||||||
|
let wc: Option<WinConsole<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stdout() as ~Writer:Send);
|
||||||
|
wc.map(|w| ~w as ~Terminal<~Writer:Send>:Send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||||
|
/// opened.
|
||||||
|
pub fn stderr() -> Option<~Terminal<~Writer:Send>:Send> {
|
||||||
|
let ti: Option<TerminfoTerminal<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stderr() as ~Writer:Send);
|
||||||
|
ti.map(|t| ~t as ~Terminal<~Writer:Send>:Send)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
|
||||||
|
/// opened.
|
||||||
|
pub fn stderr() -> Option<~Terminal<~Writer:Send>:Send> {
|
||||||
|
let ti: Option<TerminfoTerminal<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stderr() as ~Writer:Send);
|
||||||
|
|
||||||
|
match ti {
|
||||||
|
Some(t) => Some(~t as ~Terminal<~Writer:Send>:Send),
|
||||||
|
None => {
|
||||||
|
let wc: Option<WinConsole<~Writer:Send>>
|
||||||
|
= Terminal::new(~std::io::stderr() as ~Writer:Send);
|
||||||
|
wc.map(|w| ~w as ~Terminal<~Writer:Send>:Send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Terminal color definitions
|
/// Terminal color definitions
|
||||||
pub mod color {
|
pub mod color {
|
||||||
|
@ -91,72 +170,13 @@ pub mod attr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
/// A terminal with similar capabilities to an ANSI Terminal
|
||||||
match attr {
|
/// (foreground/background colors etc).
|
||||||
attr::Bold => "bold",
|
pub trait Terminal<T: Writer>: Writer {
|
||||||
attr::Dim => "dim",
|
/// Returns `None` whenever the terminal cannot be created for some
|
||||||
attr::Italic(true) => "sitm",
|
/// reason.
|
||||||
attr::Italic(false) => "ritm",
|
fn new(out: T) -> Option<Self>;
|
||||||
attr::Underline(true) => "smul",
|
|
||||||
attr::Underline(false) => "rmul",
|
|
||||||
attr::Blink => "blink",
|
|
||||||
attr::Standout(true) => "smso",
|
|
||||||
attr::Standout(false) => "rmso",
|
|
||||||
attr::Reverse => "rev",
|
|
||||||
attr::Secure => "invis",
|
|
||||||
attr::ForegroundColor(_) => "setaf",
|
|
||||||
attr::BackgroundColor(_) => "setab"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Terminal that knows how many colors it supports, with a reference to its
|
|
||||||
/// parsed TermInfo database record.
|
|
||||||
pub struct Terminal<T> {
|
|
||||||
num_colors: u16,
|
|
||||||
out: T,
|
|
||||||
ti: Box<TermInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Writer> Terminal<T> {
|
|
||||||
/// Returns a wrapped output stream (`Terminal<T>`) as a `Result`.
|
|
||||||
///
|
|
||||||
/// Returns `Err()` if the TERM environment variable is undefined.
|
|
||||||
/// TERM should be set to something like `xterm-color` or `screen-256color`.
|
|
||||||
///
|
|
||||||
/// Returns `Err()` on failure to open the terminfo database correctly.
|
|
||||||
/// Also, in the event that the individual terminfo database entry can not
|
|
||||||
/// be parsed.
|
|
||||||
pub fn new(out: T) -> Result<Terminal<T>, StrBuf> {
|
|
||||||
let term = match os::getenv("TERM") {
|
|
||||||
Some(t) => t,
|
|
||||||
None => {
|
|
||||||
return Err("TERM environment variable undefined".to_strbuf())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = match open(term) {
|
|
||||||
Ok(file) => file,
|
|
||||||
Err(err) => {
|
|
||||||
if "cygwin" == term { // msys terminal
|
|
||||||
return Ok(Terminal {
|
|
||||||
out: out,
|
|
||||||
ti: msys_terminfo(),
|
|
||||||
num_colors: 8
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let inf = try!(parse(&mut file, false));
|
|
||||||
|
|
||||||
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
|
|
||||||
&& inf.strings.find_equiv(&("setab")).is_some() {
|
|
||||||
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
|
|
||||||
} else { 0 };
|
|
||||||
|
|
||||||
return Ok(Terminal {out: out, ti: inf, num_colors: nc});
|
|
||||||
}
|
|
||||||
/// Sets the foreground color to the given color.
|
/// Sets the foreground color to the given color.
|
||||||
///
|
///
|
||||||
/// If the color is a bright color, but the terminal only supports 8 colors,
|
/// If the color is a bright color, but the terminal only supports 8 colors,
|
||||||
|
@ -164,22 +184,8 @@ impl<T: Writer> Terminal<T> {
|
||||||
///
|
///
|
||||||
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
||||||
/// if there was an I/O error.
|
/// if there was an I/O error.
|
||||||
pub fn fg(&mut self, color: color::Color) -> io::IoResult<bool> {
|
fn fg(&mut self, color: color::Color) -> IoResult<bool>;
|
||||||
let color = self.dim_if_necessary(color);
|
|
||||||
if self.num_colors > color {
|
|
||||||
let s = expand(self.ti
|
|
||||||
.strings
|
|
||||||
.find_equiv(&("setaf"))
|
|
||||||
.unwrap()
|
|
||||||
.as_slice(),
|
|
||||||
[Number(color as int)], &mut Variables::new());
|
|
||||||
if s.is_ok() {
|
|
||||||
try!(self.out.write(s.unwrap().as_slice()));
|
|
||||||
return Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
/// Sets the background color to the given color.
|
/// Sets the background color to the given color.
|
||||||
///
|
///
|
||||||
/// If the color is a bright color, but the terminal only supports 8 colors,
|
/// If the color is a bright color, but the terminal only supports 8 colors,
|
||||||
|
@ -187,104 +193,26 @@ impl<T: Writer> Terminal<T> {
|
||||||
///
|
///
|
||||||
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
|
||||||
/// if there was an I/O error.
|
/// if there was an I/O error.
|
||||||
pub fn bg(&mut self, color: color::Color) -> io::IoResult<bool> {
|
fn bg(&mut self, color: color::Color) -> IoResult<bool>;
|
||||||
let color = self.dim_if_necessary(color);
|
|
||||||
if self.num_colors > color {
|
|
||||||
let s = expand(self.ti
|
|
||||||
.strings
|
|
||||||
.find_equiv(&("setab"))
|
|
||||||
.unwrap()
|
|
||||||
.as_slice(),
|
|
||||||
[Number(color as int)], &mut Variables::new());
|
|
||||||
if s.is_ok() {
|
|
||||||
try!(self.out.write(s.unwrap().as_slice()));
|
|
||||||
return Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the given terminal attribute, if supported.
|
/// Sets the given terminal attribute, if supported. Returns `Ok(true)`
|
||||||
/// Returns `Ok(true)` if the attribute was supported, `Ok(false)` otherwise,
|
/// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
|
||||||
/// and `Err(e)` if there was an I/O error.
|
/// there was an I/O error.
|
||||||
pub fn attr(&mut self, attr: attr::Attr) -> io::IoResult<bool> {
|
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool>;
|
||||||
match attr {
|
|
||||||
attr::ForegroundColor(c) => self.fg(c),
|
|
||||||
attr::BackgroundColor(c) => self.bg(c),
|
|
||||||
_ => {
|
|
||||||
let cap = cap_for_attr(attr);
|
|
||||||
let parm = self.ti.strings.find_equiv(&cap);
|
|
||||||
if parm.is_some() {
|
|
||||||
let s = expand(parm.unwrap().as_slice(),
|
|
||||||
[],
|
|
||||||
&mut Variables::new());
|
|
||||||
if s.is_ok() {
|
|
||||||
try!(self.out.write(s.unwrap().as_slice()));
|
|
||||||
return Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the given terminal attribute is supported.
|
/// Returns whether the given terminal attribute is supported.
|
||||||
pub fn supports_attr(&self, attr: attr::Attr) -> bool {
|
fn supports_attr(&self, attr: attr::Attr) -> bool;
|
||||||
match attr {
|
|
||||||
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
|
||||||
self.num_colors > 0
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let cap = cap_for_attr(attr);
|
|
||||||
self.ti.strings.find_equiv(&cap).is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets all terminal attributes and color to the default.
|
/// Resets all terminal attributes and color to the default.
|
||||||
/// Returns `Ok()`.
|
/// Returns `Ok()`.
|
||||||
pub fn reset(&mut self) -> io::IoResult<()> {
|
fn reset(&mut self) -> IoResult<()>;
|
||||||
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
|
|
||||||
if cap.is_none() {
|
|
||||||
// are there any terminals that have color/attrs and not sgr0?
|
|
||||||
// Try falling back to sgr, then op
|
|
||||||
cap = self.ti.strings.find_equiv(&("sgr"));
|
|
||||||
if cap.is_none() {
|
|
||||||
cap = self.ti.strings.find_equiv(&("op"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let s = cap.map_or(Err("can't find terminfo capability \
|
|
||||||
`sgr0`".to_strbuf()), |op| {
|
|
||||||
expand(op.as_slice(), [], &mut Variables::new())
|
|
||||||
});
|
|
||||||
if s.is_ok() {
|
|
||||||
return self.out.write(s.unwrap().as_slice())
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
/// Returns the contained stream, destroying the `Terminal`
|
||||||
if color >= self.num_colors && color >= 8 && color < 16 {
|
fn unwrap(self) -> T;
|
||||||
color-8
|
|
||||||
} else { color }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the contained stream
|
|
||||||
pub fn unwrap(self) -> T { self.out }
|
|
||||||
|
|
||||||
/// Gets an immutable reference to the stream inside
|
/// Gets an immutable reference to the stream inside
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a T { &self.out }
|
fn get_ref<'a>(&'a self) -> &'a T;
|
||||||
|
|
||||||
/// Gets a mutable reference to the stream inside
|
/// Gets a mutable reference to the stream inside
|
||||||
pub fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
|
fn get_mut<'a>(&'a mut self) -> &'a mut T;
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Writer> Writer for Terminal<T> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
|
|
||||||
self.out.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::IoResult<()> {
|
|
||||||
self.out.flush()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,16 @@
|
||||||
//! Terminfo database interface.
|
//! Terminfo database interface.
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::os;
|
||||||
|
|
||||||
|
use attr;
|
||||||
|
use color;
|
||||||
|
use Terminal;
|
||||||
|
use self::searcher::open;
|
||||||
|
use self::parser::compiled::{parse, msys_terminfo};
|
||||||
|
use self::parm::{expand, Number, Variables};
|
||||||
|
|
||||||
|
|
||||||
/// A parsed terminfo database entry.
|
/// A parsed terminfo database entry.
|
||||||
pub struct TermInfo {
|
pub struct TermInfo {
|
||||||
|
@ -32,3 +42,179 @@ pub mod parser {
|
||||||
pub mod compiled;
|
pub mod compiled;
|
||||||
}
|
}
|
||||||
pub mod parm;
|
pub mod parm;
|
||||||
|
|
||||||
|
|
||||||
|
fn cap_for_attr(attr: attr::Attr) -> &'static str {
|
||||||
|
match attr {
|
||||||
|
attr::Bold => "bold",
|
||||||
|
attr::Dim => "dim",
|
||||||
|
attr::Italic(true) => "sitm",
|
||||||
|
attr::Italic(false) => "ritm",
|
||||||
|
attr::Underline(true) => "smul",
|
||||||
|
attr::Underline(false) => "rmul",
|
||||||
|
attr::Blink => "blink",
|
||||||
|
attr::Standout(true) => "smso",
|
||||||
|
attr::Standout(false) => "rmso",
|
||||||
|
attr::Reverse => "rev",
|
||||||
|
attr::Secure => "invis",
|
||||||
|
attr::ForegroundColor(_) => "setaf",
|
||||||
|
attr::BackgroundColor(_) => "setab"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Terminal that knows how many colors it supports, with a reference to its
|
||||||
|
/// parsed Terminfo database record.
|
||||||
|
pub struct TerminfoTerminal<T> {
|
||||||
|
num_colors: u16,
|
||||||
|
out: T,
|
||||||
|
ti: ~TermInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Writer> Terminal<T> for TerminfoTerminal<T> {
|
||||||
|
fn new(out: T) -> Option<TerminfoTerminal<T>> {
|
||||||
|
let term = match os::getenv("TERM") {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
debug!("TERM environment variable not defined");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = open(term);
|
||||||
|
if entry.is_err() {
|
||||||
|
if os::getenv("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
|
||||||
|
// msys terminal
|
||||||
|
return Some(TerminfoTerminal {out: out, ti: msys_terminfo(), num_colors: 8});
|
||||||
|
}
|
||||||
|
debug!("error finding terminfo entry: {}", entry.unwrap_err());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = entry.unwrap();
|
||||||
|
let ti = parse(&mut file, false);
|
||||||
|
if ti.is_err() {
|
||||||
|
debug!("error parsing terminfo entry: {}", ti.unwrap_err());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inf = ti.unwrap();
|
||||||
|
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
|
||||||
|
&& inf.strings.find_equiv(&("setab")).is_some() {
|
||||||
|
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
|
||||||
|
} else { 0 };
|
||||||
|
|
||||||
|
return Some(TerminfoTerminal {out: out, ti: inf, num_colors: nc});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||||
|
let color = self.dim_if_necessary(color);
|
||||||
|
if self.num_colors > color {
|
||||||
|
let s = expand(self.ti
|
||||||
|
.strings
|
||||||
|
.find_equiv(&("setaf"))
|
||||||
|
.unwrap()
|
||||||
|
.as_slice(),
|
||||||
|
[Number(color as int)], &mut Variables::new());
|
||||||
|
if s.is_ok() {
|
||||||
|
try!(self.out.write(s.unwrap().as_slice()));
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||||
|
let color = self.dim_if_necessary(color);
|
||||||
|
if self.num_colors > color {
|
||||||
|
let s = expand(self.ti
|
||||||
|
.strings
|
||||||
|
.find_equiv(&("setab"))
|
||||||
|
.unwrap()
|
||||||
|
.as_slice(),
|
||||||
|
[Number(color as int)], &mut Variables::new());
|
||||||
|
if s.is_ok() {
|
||||||
|
try!(self.out.write(s.unwrap().as_slice()));
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
|
||||||
|
match attr {
|
||||||
|
attr::ForegroundColor(c) => self.fg(c),
|
||||||
|
attr::BackgroundColor(c) => self.bg(c),
|
||||||
|
_ => {
|
||||||
|
let cap = cap_for_attr(attr);
|
||||||
|
let parm = self.ti.strings.find_equiv(&cap);
|
||||||
|
if parm.is_some() {
|
||||||
|
let s = expand(parm.unwrap().as_slice(),
|
||||||
|
[],
|
||||||
|
&mut Variables::new());
|
||||||
|
if s.is_ok() {
|
||||||
|
try!(self.out.write(s.unwrap().as_slice()));
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||||
|
match attr {
|
||||||
|
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
|
||||||
|
self.num_colors > 0
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let cap = cap_for_attr(attr);
|
||||||
|
self.ti.strings.find_equiv(&cap).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> IoResult<()> {
|
||||||
|
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
|
||||||
|
if cap.is_none() {
|
||||||
|
// are there any terminals that have color/attrs and not sgr0?
|
||||||
|
// Try falling back to sgr, then op
|
||||||
|
cap = self.ti.strings.find_equiv(&("sgr"));
|
||||||
|
if cap.is_none() {
|
||||||
|
cap = self.ti.strings.find_equiv(&("op"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let s = cap.map_or(Err(~"can't find terminfo capability `sgr0`"), |op| {
|
||||||
|
expand(op.as_slice(), [], &mut Variables::new())
|
||||||
|
});
|
||||||
|
if s.is_ok() {
|
||||||
|
return self.out.write(s.unwrap().as_slice())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap(self) -> T { self.out }
|
||||||
|
|
||||||
|
fn get_ref<'a>(&'a self) -> &'a T { &self.out }
|
||||||
|
|
||||||
|
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Writer> TerminfoTerminal<T> {
|
||||||
|
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
|
||||||
|
if color >= self.num_colors && color >= 8 && color < 16 {
|
||||||
|
color-8
|
||||||
|
} else { color }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: Writer> Writer for TerminfoTerminal<T> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||||
|
self.out.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> IoResult<()> {
|
||||||
|
self.out.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Windows console handling
|
||||||
|
|
||||||
|
// FIXME (#13400): this is only a tiny fraction of the win32 console api
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
use attr;
|
||||||
|
use color;
|
||||||
|
use Terminal;
|
||||||
|
|
||||||
|
pub struct WinConsole<T> {
|
||||||
|
buf: T,
|
||||||
|
foreground: color::Color,
|
||||||
|
background: color::Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
fn SetConsoleTextAttribute(handle: libc::HANDLE, attr: libc::WORD) -> libc::BOOL;
|
||||||
|
fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color_to_bits(color: color::Color) -> u16 {
|
||||||
|
// magic numbers from mingw-w64's wincon.h
|
||||||
|
|
||||||
|
let bits = match color % 8 {
|
||||||
|
color::BLACK => 0,
|
||||||
|
color::BLUE => 0x1,
|
||||||
|
color::GREEN => 0x2,
|
||||||
|
color::RED => 0x4,
|
||||||
|
color::YELLOW => 0x2 | 0x4,
|
||||||
|
color::MAGENTA => 0x1 | 0x4,
|
||||||
|
color::CYAN => 0x1 | 0x2,
|
||||||
|
color::WHITE => 0x1 | 0x2 | 0x4,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if color >= 8 {
|
||||||
|
bits | 0x8
|
||||||
|
} else {
|
||||||
|
bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Writer> WinConsole<T> {
|
||||||
|
fn apply(&mut self) {
|
||||||
|
let mut accum: libc::WORD = 0;
|
||||||
|
accum |= color_to_bits(self.foreground);
|
||||||
|
accum |= color_to_bits(self.background) << 4;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Magic -11 means stdout, from
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
|
||||||
|
//
|
||||||
|
// You may be wondering, "but what about stderr?", and the answer
|
||||||
|
// to that is that setting terminal attributes on the stdout
|
||||||
|
// handle also sets them for stderr, since they go to the same
|
||||||
|
// terminal! Admittedly, this is fragile, since stderr could be
|
||||||
|
// redirected to a different console. This is good enough for
|
||||||
|
// rustc though. See #13400.
|
||||||
|
let out = GetStdHandle(-11);
|
||||||
|
SetConsoleTextAttribute(out, accum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Writer> Writer for WinConsole<T> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||||
|
self.buf.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> IoResult<()> {
|
||||||
|
self.buf.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Writer> Terminal<T> for WinConsole<T> {
|
||||||
|
fn new(out: T) -> Option<WinConsole<T>> {
|
||||||
|
Some(WinConsole { buf: out, foreground: color::WHITE, background: color::BLACK })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||||
|
self.foreground = color;
|
||||||
|
self.apply();
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
|
||||||
|
self.background = color;
|
||||||
|
self.apply();
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
|
||||||
|
match attr {
|
||||||
|
attr::ForegroundColor(f) => {
|
||||||
|
self.foreground = f;
|
||||||
|
self.apply();
|
||||||
|
Ok(true)
|
||||||
|
},
|
||||||
|
attr::BackgroundColor(b) => {
|
||||||
|
self.background = b;
|
||||||
|
self.apply();
|
||||||
|
Ok(true)
|
||||||
|
},
|
||||||
|
_ => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supports_attr(&self, attr: attr::Attr) -> bool {
|
||||||
|
// it claims support for underscore and reverse video, but I can't get
|
||||||
|
// it to do anything -cmr
|
||||||
|
match attr {
|
||||||
|
attr::ForegroundColor(_) | attr::BackgroundColor(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> IoResult<()> {
|
||||||
|
self.foreground = color::WHITE;
|
||||||
|
self.background = color::BLACK;
|
||||||
|
self.apply();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap(self) -> T { self.buf }
|
||||||
|
|
||||||
|
fn get_ref<'a>(&'a self) -> &'a T { &self.buf }
|
||||||
|
|
||||||
|
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }
|
||||||
|
}
|
Loading…
Reference in New Issue