Skip to content

Commit

Permalink
Merge pull request #116 from epage/ants
Browse files Browse the repository at this point in the history
feat(wincon): Expose lower level calls
  • Loading branch information
epage authored Aug 23, 2023
2 parents bd2a983 + 3eeb5be commit 4356825
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 139 deletions.
2 changes: 1 addition & 1 deletion crates/anstyle-wincon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod console;
mod lockable;
mod stream;
#[cfg(windows)]
mod windows;
pub mod windows;

pub use console::Console;
pub use lockable::Lockable;
Expand Down
13 changes: 3 additions & 10 deletions crates/anstyle-wincon/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,15 @@ impl WinconStream for std::fs::File {

#[cfg(windows)]
mod wincon {
use std::os::windows::io::{AsHandle, AsRawHandle};
use std::os::windows::io::AsHandle;

pub(super) fn set_colors<S: AsHandle>(
stream: &mut S,
fg: Option<anstyle::AnsiColor>,
bg: Option<anstyle::AnsiColor>,
) -> std::io::Result<()> {
let handle = stream.as_handle();
let handle = handle.as_raw_handle();
if let (Some(fg), Some(bg)) = (fg, bg) {
let attributes = crate::windows::set_colors(fg, bg);
crate::windows::set_console_text_attributes(handle, attributes)
crate::windows::set_colors(stream, fg, bg)
} else {
Ok(())
}
Expand All @@ -120,11 +117,7 @@ mod wincon {
pub(super) fn get_colors<S: AsHandle>(
stream: &S,
) -> std::io::Result<(Option<anstyle::AnsiColor>, Option<anstyle::AnsiColor>)> {
let handle = stream.as_handle();
let handle = handle.as_raw_handle();
let info = crate::windows::get_screen_buffer_info(handle)?;
let (fg, bg) = crate::windows::get_colors(&info);
Ok((Some(fg), Some(bg)))
crate::windows::get_colors(stream).map(|(fg, bg)| (Some(fg), Some(bg)))
}
}

Expand Down
292 changes: 164 additions & 128 deletions crates/anstyle-wincon/src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,147 +1,183 @@
use windows_sys::Win32::System::Console::CONSOLE_CHARACTER_ATTRIBUTES;
use windows_sys::Win32::System::Console::CONSOLE_SCREEN_BUFFER_INFO;
use windows_sys::Win32::System::Console::FOREGROUND_BLUE;
use windows_sys::Win32::System::Console::FOREGROUND_GREEN;
use windows_sys::Win32::System::Console::FOREGROUND_INTENSITY;
use windows_sys::Win32::System::Console::FOREGROUND_RED;
use std::os::windows::io::AsHandle;
use std::os::windows::io::AsRawHandle;

use std::os::windows::io::RawHandle;
pub fn set_colors<S: AsHandle>(
stream: &mut S,
fg: anstyle::AnsiColor,
bg: anstyle::AnsiColor,
) -> std::io::Result<()> {
let handle = stream.as_handle();
let handle = handle.as_raw_handle();
let attributes = inner::set_colors(fg, bg);
inner::set_console_text_attributes(handle, attributes)
}

pub fn get_colors<S: AsHandle>(
stream: &S,
) -> std::io::Result<(anstyle::AnsiColor, anstyle::AnsiColor)> {
let handle = stream.as_handle();
let handle = handle.as_raw_handle();
let info = inner::get_screen_buffer_info(handle)?;
let (fg, bg) = inner::get_colors(&info);
Ok((fg, bg))
}

const FOREGROUND_CYAN: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_BLUE | FOREGROUND_GREEN;
const FOREGROUND_MAGENTA: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_BLUE | FOREGROUND_RED;
const FOREGROUND_YELLOW: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_GREEN | FOREGROUND_RED;
const FOREGROUND_WHITE: CONSOLE_CHARACTER_ATTRIBUTES =
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
mod inner {
use windows_sys::Win32::System::Console::CONSOLE_CHARACTER_ATTRIBUTES;
use windows_sys::Win32::System::Console::CONSOLE_SCREEN_BUFFER_INFO;
use windows_sys::Win32::System::Console::FOREGROUND_BLUE;
use windows_sys::Win32::System::Console::FOREGROUND_GREEN;
use windows_sys::Win32::System::Console::FOREGROUND_INTENSITY;
use windows_sys::Win32::System::Console::FOREGROUND_RED;

pub fn get_screen_buffer_info(handle: RawHandle) -> std::io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
unsafe {
let handle = std::mem::transmute(handle);
if handle == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"console is detached",
));
}
use std::os::windows::io::RawHandle;

let mut info: CONSOLE_SCREEN_BUFFER_INFO = std::mem::zeroed();
if windows_sys::Win32::System::Console::GetConsoleScreenBufferInfo(handle, &mut info) != 0 {
Ok(info)
} else {
Err(std::io::Error::last_os_error())
const FOREGROUND_CYAN: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_BLUE | FOREGROUND_GREEN;
const FOREGROUND_MAGENTA: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_BLUE | FOREGROUND_RED;
const FOREGROUND_YELLOW: CONSOLE_CHARACTER_ATTRIBUTES = FOREGROUND_GREEN | FOREGROUND_RED;
const FOREGROUND_WHITE: CONSOLE_CHARACTER_ATTRIBUTES =
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;

pub fn get_screen_buffer_info(
handle: RawHandle,
) -> std::io::Result<CONSOLE_SCREEN_BUFFER_INFO> {
unsafe {
let handle = std::mem::transmute(handle);
if handle == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"console is detached",
));
}

let mut info: CONSOLE_SCREEN_BUFFER_INFO = std::mem::zeroed();
if windows_sys::Win32::System::Console::GetConsoleScreenBufferInfo(handle, &mut info)
!= 0
{
Ok(info)
} else {
Err(std::io::Error::last_os_error())
}
}
}
}

pub fn set_console_text_attributes(
handle: RawHandle,
attributes: CONSOLE_CHARACTER_ATTRIBUTES,
) -> std::io::Result<()> {
unsafe {
let handle = std::mem::transmute(handle);
if handle == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"console is detached",
));
}
pub fn set_console_text_attributes(
handle: RawHandle,
attributes: CONSOLE_CHARACTER_ATTRIBUTES,
) -> std::io::Result<()> {
unsafe {
let handle = std::mem::transmute(handle);
if handle == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"console is detached",
));
}

if windows_sys::Win32::System::Console::SetConsoleTextAttribute(handle, attributes) != 0 {
Ok(())
} else {
Err(std::io::Error::last_os_error())
if windows_sys::Win32::System::Console::SetConsoleTextAttribute(handle, attributes) != 0
{
Ok(())
} else {
Err(std::io::Error::last_os_error())
}
}
}
}

pub fn get_colors(info: &CONSOLE_SCREEN_BUFFER_INFO) -> (anstyle::AnsiColor, anstyle::AnsiColor) {
let attributes = info.wAttributes;
let bg = from_nibble(attributes >> 4);
let fg = from_nibble(attributes);
(fg, bg)
}
pub fn get_colors(
info: &CONSOLE_SCREEN_BUFFER_INFO,
) -> (anstyle::AnsiColor, anstyle::AnsiColor) {
let attributes = info.wAttributes;
let bg = from_nibble(attributes >> 4);
let fg = from_nibble(attributes);
(fg, bg)
}

pub fn set_colors(fg: anstyle::AnsiColor, bg: anstyle::AnsiColor) -> CONSOLE_CHARACTER_ATTRIBUTES {
to_nibble(bg) << 4 | to_nibble(fg)
}
pub fn set_colors(
fg: anstyle::AnsiColor,
bg: anstyle::AnsiColor,
) -> CONSOLE_CHARACTER_ATTRIBUTES {
to_nibble(bg) << 4 | to_nibble(fg)
}

fn from_nibble(color: CONSOLE_CHARACTER_ATTRIBUTES) -> anstyle::AnsiColor {
if color & FOREGROUND_WHITE == FOREGROUND_WHITE {
// 3 bits high
anstyle::AnsiColor::White
} else if color & FOREGROUND_CYAN == FOREGROUND_CYAN {
// 2 bits high
anstyle::AnsiColor::Cyan
} else if color & FOREGROUND_YELLOW == FOREGROUND_YELLOW {
// 2 bits high
anstyle::AnsiColor::Yellow
} else if color & FOREGROUND_MAGENTA == FOREGROUND_MAGENTA {
// 2 bits high
anstyle::AnsiColor::Magenta
} else if color & FOREGROUND_RED == FOREGROUND_RED {
// 1 bit high
anstyle::AnsiColor::Red
} else if color & FOREGROUND_GREEN == FOREGROUND_GREEN {
// 1 bit high
anstyle::AnsiColor::Green
} else if color & FOREGROUND_BLUE == FOREGROUND_BLUE {
// 1 bit high
anstyle::AnsiColor::Blue
} else {
// 0 bits high
anstyle::AnsiColor::Black
fn from_nibble(color: CONSOLE_CHARACTER_ATTRIBUTES) -> anstyle::AnsiColor {
if color & FOREGROUND_WHITE == FOREGROUND_WHITE {
// 3 bits high
anstyle::AnsiColor::White
} else if color & FOREGROUND_CYAN == FOREGROUND_CYAN {
// 2 bits high
anstyle::AnsiColor::Cyan
} else if color & FOREGROUND_YELLOW == FOREGROUND_YELLOW {
// 2 bits high
anstyle::AnsiColor::Yellow
} else if color & FOREGROUND_MAGENTA == FOREGROUND_MAGENTA {
// 2 bits high
anstyle::AnsiColor::Magenta
} else if color & FOREGROUND_RED == FOREGROUND_RED {
// 1 bit high
anstyle::AnsiColor::Red
} else if color & FOREGROUND_GREEN == FOREGROUND_GREEN {
// 1 bit high
anstyle::AnsiColor::Green
} else if color & FOREGROUND_BLUE == FOREGROUND_BLUE {
// 1 bit high
anstyle::AnsiColor::Blue
} else {
// 0 bits high
anstyle::AnsiColor::Black
}
.bright(color & FOREGROUND_INTENSITY == FOREGROUND_INTENSITY)
}
.bright(color & FOREGROUND_INTENSITY == FOREGROUND_INTENSITY)
}

fn to_nibble(color: anstyle::AnsiColor) -> CONSOLE_CHARACTER_ATTRIBUTES {
let mut attributes = 0;
attributes |= match color.bright(false) {
anstyle::AnsiColor::Black => 0,
anstyle::AnsiColor::Red => FOREGROUND_RED,
anstyle::AnsiColor::Green => FOREGROUND_GREEN,
anstyle::AnsiColor::Yellow => FOREGROUND_YELLOW,
anstyle::AnsiColor::Blue => FOREGROUND_BLUE,
anstyle::AnsiColor::Magenta => FOREGROUND_MAGENTA,
anstyle::AnsiColor::Cyan => FOREGROUND_CYAN,
anstyle::AnsiColor::White => FOREGROUND_WHITE,
anstyle::AnsiColor::BrightBlack
| anstyle::AnsiColor::BrightRed
| anstyle::AnsiColor::BrightGreen
| anstyle::AnsiColor::BrightYellow
| anstyle::AnsiColor::BrightBlue
| anstyle::AnsiColor::BrightMagenta
| anstyle::AnsiColor::BrightCyan
| anstyle::AnsiColor::BrightWhite => unreachable!("brights were toggled off"),
};
if color.is_bright() {
attributes |= FOREGROUND_INTENSITY;
fn to_nibble(color: anstyle::AnsiColor) -> CONSOLE_CHARACTER_ATTRIBUTES {
let mut attributes = 0;
attributes |= match color.bright(false) {
anstyle::AnsiColor::Black => 0,
anstyle::AnsiColor::Red => FOREGROUND_RED,
anstyle::AnsiColor::Green => FOREGROUND_GREEN,
anstyle::AnsiColor::Yellow => FOREGROUND_YELLOW,
anstyle::AnsiColor::Blue => FOREGROUND_BLUE,
anstyle::AnsiColor::Magenta => FOREGROUND_MAGENTA,
anstyle::AnsiColor::Cyan => FOREGROUND_CYAN,
anstyle::AnsiColor::White => FOREGROUND_WHITE,
anstyle::AnsiColor::BrightBlack
| anstyle::AnsiColor::BrightRed
| anstyle::AnsiColor::BrightGreen
| anstyle::AnsiColor::BrightYellow
| anstyle::AnsiColor::BrightBlue
| anstyle::AnsiColor::BrightMagenta
| anstyle::AnsiColor::BrightCyan
| anstyle::AnsiColor::BrightWhite => unreachable!("brights were toggled off"),
};
if color.is_bright() {
attributes |= FOREGROUND_INTENSITY;
}
attributes
}
attributes
}

#[test]
fn to_from_nibble() {
const COLORS: [anstyle::AnsiColor; 16] = [
anstyle::AnsiColor::Black,
anstyle::AnsiColor::Red,
anstyle::AnsiColor::Green,
anstyle::AnsiColor::Yellow,
anstyle::AnsiColor::Blue,
anstyle::AnsiColor::Magenta,
anstyle::AnsiColor::Cyan,
anstyle::AnsiColor::White,
anstyle::AnsiColor::BrightBlack,
anstyle::AnsiColor::BrightRed,
anstyle::AnsiColor::BrightGreen,
anstyle::AnsiColor::BrightYellow,
anstyle::AnsiColor::BrightBlue,
anstyle::AnsiColor::BrightMagenta,
anstyle::AnsiColor::BrightCyan,
anstyle::AnsiColor::BrightWhite,
];
for expected in COLORS {
let nibble = to_nibble(expected);
let actual = from_nibble(nibble);
assert_eq!(expected, actual, "Intermediate: {}", nibble);
#[test]
fn to_from_nibble() {
const COLORS: [anstyle::AnsiColor; 16] = [
anstyle::AnsiColor::Black,
anstyle::AnsiColor::Red,
anstyle::AnsiColor::Green,
anstyle::AnsiColor::Yellow,
anstyle::AnsiColor::Blue,
anstyle::AnsiColor::Magenta,
anstyle::AnsiColor::Cyan,
anstyle::AnsiColor::White,
anstyle::AnsiColor::BrightBlack,
anstyle::AnsiColor::BrightRed,
anstyle::AnsiColor::BrightGreen,
anstyle::AnsiColor::BrightYellow,
anstyle::AnsiColor::BrightBlue,
anstyle::AnsiColor::BrightMagenta,
anstyle::AnsiColor::BrightCyan,
anstyle::AnsiColor::BrightWhite,
];
for expected in COLORS {
let nibble = to_nibble(expected);
let actual = from_nibble(nibble);
assert_eq!(expected, actual, "Intermediate: {}", nibble);
}
}
}

0 comments on commit 4356825

Please sign in to comment.