1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-01-08 13:23:34 +02:00

Make wincolor crate compilable on non-Windows platforms.

This commit is contained in:
Andrew Gallant 2016-11-20 15:44:19 -05:00
parent d06f84ced3
commit df72d8d1e0
2 changed files with 231 additions and 221 deletions

View File

@ -3,6 +3,8 @@ This crate provides a safe and simple Windows specific API to control
text attributes in the Windows console. Text attributes are limited to
foreground/background colors, as well as whether to make colors intense or not.
Note that on non-Windows platforms, this crate is empty but will compile.
# Example
```no_run
@ -15,228 +17,13 @@ con.reset().unwrap();
println!("This text will be normal.");
```
*/
#[cfg(windows)]
extern crate kernel32;
#[cfg(windows)]
extern crate winapi;
use std::io;
use std::mem;
#[cfg(windows)]
pub use win::*;
use winapi::{DWORD, HANDLE, WORD};
use winapi::winbase::STD_OUTPUT_HANDLE;
use winapi::wincon::{
FOREGROUND_BLUE as FG_BLUE,
FOREGROUND_GREEN as FG_GREEN,
FOREGROUND_RED as FG_RED,
FOREGROUND_INTENSITY as FG_INTENSITY,
};
const FG_CYAN: DWORD = FG_BLUE | FG_GREEN;
const FG_MAGENTA: DWORD = FG_BLUE | FG_RED;
const FG_YELLOW: DWORD = FG_GREEN | FG_RED;
const FG_WHITE: DWORD = FG_BLUE | FG_GREEN | FG_RED;
/// A Windows console.
///
/// This represents a very limited set of functionality available to a Windows
/// console. In particular, it can only change text attributes such as color
/// and intensity.
///
/// There is no way to "write" to this console. Simply write to
/// stdout or stderr instead, while interleaving instructions to the console
/// to change text attributes.
///
/// A common pitfall when using a console is to forget to flush writes to
/// stdout before setting new text attributes.
#[derive(Debug)]
pub struct Console {
handle: HANDLE,
start_attr: TextAttributes,
cur_attr: TextAttributes,
}
unsafe impl Send for Console {}
impl Drop for Console {
fn drop(&mut self) {
unsafe { kernel32::CloseHandle(self.handle); }
}
}
impl Console {
/// Create a new Console to stdout.
///
/// If there was a problem creating the console, then an error is returned.
pub fn stdout() -> io::Result<Console> {
let mut info = unsafe { mem::zeroed() };
let (handle, res) = unsafe {
let handle = kernel32::GetStdHandle(STD_OUTPUT_HANDLE);
(handle, kernel32::GetConsoleScreenBufferInfo(handle, &mut info))
};
if res == 0 {
return Err(io::Error::last_os_error());
}
let attr = TextAttributes::from_word(info.wAttributes);
Ok(Console {
handle: handle,
start_attr: attr,
cur_attr: attr,
})
}
/// Applies the current text attributes.
fn set(&mut self) -> io::Result<()> {
let attr = self.cur_attr.to_word();
let res = unsafe {
kernel32::SetConsoleTextAttribute(self.handle, attr)
};
if res == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
/// Apply the given intensity and color attributes to the console
/// foreground.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.fg_color = color;
self.cur_attr.fg_intense = intense;
self.set()
}
/// Apply the given intensity and color attributes to the console
/// background.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.bg_color = color;
self.cur_attr.bg_intense = intense;
self.set()
}
/// Reset the console text attributes to their original settings.
///
/// The original settings correspond to the text attributes on the console
/// when this `Console` value was created.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn reset(&mut self) -> io::Result<()> {
self.cur_attr = self.start_attr;
self.set()
}
}
/// A representation of text attributes for the Windows console.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct TextAttributes {
fg_color: Color,
fg_intense: Intense,
bg_color: Color,
bg_intense: Intense,
}
impl TextAttributes {
fn to_word(&self) -> WORD {
let mut w = 0;
w |= self.fg_color.to_fg();
w |= self.fg_intense.to_fg();
w |= self.bg_color.to_bg();
w |= self.bg_intense.to_bg();
w as WORD
}
fn from_word(word: WORD) -> TextAttributes {
let attr = word as DWORD;
TextAttributes {
fg_color: Color::from_fg(attr),
fg_intense: Intense::from_fg(attr),
bg_color: Color::from_bg(attr),
bg_intense: Intense::from_bg(attr),
}
}
}
/// Whether to use intense colors or not.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Intense {
Yes,
No,
}
impl Intense {
fn to_bg(&self) -> DWORD {
self.to_fg() << 4
}
fn from_bg(word: DWORD) -> Intense {
Intense::from_fg(word >> 4)
}
fn to_fg(&self) -> DWORD {
match *self {
Intense::No => 0,
Intense::Yes => FG_INTENSITY,
}
}
fn from_fg(word: DWORD) -> Intense {
if word & FG_INTENSITY > 0 {
Intense::Yes
} else {
Intense::No
}
}
}
/// The set of available colors for use with a Windows console.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Color {
Black,
Blue,
Green,
Red,
Cyan,
Magenta,
Yellow,
White,
}
impl Color {
fn to_bg(&self) -> DWORD {
self.to_fg() << 4
}
fn from_bg(word: DWORD) -> Color {
Color::from_fg(word >> 4)
}
fn to_fg(&self) -> DWORD {
match *self {
Color::Black => 0,
Color::Blue => FG_BLUE,
Color::Green => FG_GREEN,
Color::Red => FG_RED,
Color::Cyan => FG_CYAN,
Color::Magenta => FG_MAGENTA,
Color::Yellow => FG_YELLOW,
Color::White => FG_WHITE,
}
}
fn from_fg(word: DWORD) -> Color {
match word & 0b111 {
FG_BLUE => Color::Blue,
FG_GREEN => Color::Green,
FG_RED => Color::Red,
FG_CYAN => Color::Cyan,
FG_MAGENTA => Color::Magenta,
FG_YELLOW => Color::Yellow,
FG_WHITE => Color::White,
_ => Color::Black,
}
}
}
#[cfg(windows)]
mod win;

223
wincolor/src/win.rs Normal file
View File

@ -0,0 +1,223 @@
use std::io;
use std::mem;
use kernel32;
use winapi::{DWORD, HANDLE, WORD};
use winapi::winbase::STD_OUTPUT_HANDLE;
use winapi::wincon::{
FOREGROUND_BLUE as FG_BLUE,
FOREGROUND_GREEN as FG_GREEN,
FOREGROUND_RED as FG_RED,
FOREGROUND_INTENSITY as FG_INTENSITY,
};
const FG_CYAN: DWORD = FG_BLUE | FG_GREEN;
const FG_MAGENTA: DWORD = FG_BLUE | FG_RED;
const FG_YELLOW: DWORD = FG_GREEN | FG_RED;
const FG_WHITE: DWORD = FG_BLUE | FG_GREEN | FG_RED;
/// A Windows console.
///
/// This represents a very limited set of functionality available to a Windows
/// console. In particular, it can only change text attributes such as color
/// and intensity.
///
/// There is no way to "write" to this console. Simply write to
/// stdout or stderr instead, while interleaving instructions to the console
/// to change text attributes.
///
/// A common pitfall when using a console is to forget to flush writes to
/// stdout before setting new text attributes.
#[derive(Debug)]
pub struct Console {
handle: HANDLE,
start_attr: TextAttributes,
cur_attr: TextAttributes,
}
unsafe impl Send for Console {}
impl Drop for Console {
fn drop(&mut self) {
unsafe { kernel32::CloseHandle(self.handle); }
}
}
impl Console {
/// Create a new Console to stdout.
///
/// If there was a problem creating the console, then an error is returned.
pub fn stdout() -> io::Result<Console> {
let mut info = unsafe { mem::zeroed() };
let (handle, res) = unsafe {
let handle = kernel32::GetStdHandle(STD_OUTPUT_HANDLE);
(handle, kernel32::GetConsoleScreenBufferInfo(handle, &mut info))
};
if res == 0 {
return Err(io::Error::last_os_error());
}
let attr = TextAttributes::from_word(info.wAttributes);
Ok(Console {
handle: handle,
start_attr: attr,
cur_attr: attr,
})
}
/// Applies the current text attributes.
fn set(&mut self) -> io::Result<()> {
let attr = self.cur_attr.to_word();
let res = unsafe {
kernel32::SetConsoleTextAttribute(self.handle, attr)
};
if res == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
/// Apply the given intensity and color attributes to the console
/// foreground.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.fg_color = color;
self.cur_attr.fg_intense = intense;
self.set()
}
/// Apply the given intensity and color attributes to the console
/// background.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
self.cur_attr.bg_color = color;
self.cur_attr.bg_intense = intense;
self.set()
}
/// Reset the console text attributes to their original settings.
///
/// The original settings correspond to the text attributes on the console
/// when this `Console` value was created.
///
/// If there was a problem setting attributes on the console, then an error
/// is returned.
pub fn reset(&mut self) -> io::Result<()> {
self.cur_attr = self.start_attr;
self.set()
}
}
/// A representation of text attributes for the Windows console.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct TextAttributes {
fg_color: Color,
fg_intense: Intense,
bg_color: Color,
bg_intense: Intense,
}
impl TextAttributes {
fn to_word(&self) -> WORD {
let mut w = 0;
w |= self.fg_color.to_fg();
w |= self.fg_intense.to_fg();
w |= self.bg_color.to_bg();
w |= self.bg_intense.to_bg();
w as WORD
}
fn from_word(word: WORD) -> TextAttributes {
let attr = word as DWORD;
TextAttributes {
fg_color: Color::from_fg(attr),
fg_intense: Intense::from_fg(attr),
bg_color: Color::from_bg(attr),
bg_intense: Intense::from_bg(attr),
}
}
}
/// Whether to use intense colors or not.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Intense {
Yes,
No,
}
impl Intense {
fn to_bg(&self) -> DWORD {
self.to_fg() << 4
}
fn from_bg(word: DWORD) -> Intense {
Intense::from_fg(word >> 4)
}
fn to_fg(&self) -> DWORD {
match *self {
Intense::No => 0,
Intense::Yes => FG_INTENSITY,
}
}
fn from_fg(word: DWORD) -> Intense {
if word & FG_INTENSITY > 0 {
Intense::Yes
} else {
Intense::No
}
}
}
/// The set of available colors for use with a Windows console.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Color {
Black,
Blue,
Green,
Red,
Cyan,
Magenta,
Yellow,
White,
}
impl Color {
fn to_bg(&self) -> DWORD {
self.to_fg() << 4
}
fn from_bg(word: DWORD) -> Color {
Color::from_fg(word >> 4)
}
fn to_fg(&self) -> DWORD {
match *self {
Color::Black => 0,
Color::Blue => FG_BLUE,
Color::Green => FG_GREEN,
Color::Red => FG_RED,
Color::Cyan => FG_CYAN,
Color::Magenta => FG_MAGENTA,
Color::Yellow => FG_YELLOW,
Color::White => FG_WHITE,
}
}
fn from_fg(word: DWORD) -> Color {
match word & 0b111 {
FG_BLUE => Color::Blue,
FG_GREEN => Color::Green,
FG_RED => Color::Red,
FG_CYAN => Color::Cyan,
FG_MAGENTA => Color::Magenta,
FG_YELLOW => Color::Yellow,
FG_WHITE => Color::White,
_ => Color::Black,
}
}
}