#![cfg(feature = "wincon")]
use super::*;
use winapi::um::wincon::{
AddConsoleAliasW, AttachConsole, CreateConsoleScreenBuffer,
FillConsoleOutputAttribute, FillConsoleOutputCharacterW, FlushConsoleInputBuffer,
FreeConsole, GenerateConsoleCtrlEvent, GetConsoleAliasW, GetConsoleAliasesLengthW,
GetConsoleAliasesW, GetConsoleCursorInfo, GetConsoleDisplayMode,
GetConsoleFontSize, GetConsoleHistoryInfo, GetConsoleOriginalTitleW,
GetConsoleProcessList, GetConsoleScreenBufferInfo, GetConsoleScreenBufferInfoEx,
GetConsoleSelectionInfo, GetConsoleTitleW, GetConsoleWindow,
GetCurrentConsoleFont, GetCurrentConsoleFontEx, GetLargestConsoleWindowSize,
GetNumberOfConsoleMouseButtons, PeekConsoleInputW, ReadConsoleOutputAttribute,
ReadConsoleOutputCharacterW, ReadConsoleOutputW, ScrollConsoleScreenBufferW,
SetConsoleActiveScreenBuffer, SetConsoleCP, SetConsoleCursorInfo,
SetConsoleCursorPosition, SetConsoleDisplayMode, SetConsoleHistoryInfo,
SetConsoleOutputCP, SetConsoleScreenBufferInfoEx, SetConsoleScreenBufferSize,
SetConsoleTextAttribute, SetConsoleTitleW, SetConsoleWindowInfo,
SetCurrentConsoleFontEx, WriteConsoleInputW, WriteConsoleOutputAttribute,
WriteConsoleOutputCharacterW, WriteConsoleOutputW, CONSOLE_TEXTMODE_BUFFER,
};
pub use winapi::um::wincon::{
ATTACH_PARENT_PROCESS, CONSOLE_CURSOR_INFO, CONSOLE_FONT_INFOEX,
CONSOLE_HISTORY_INFO, CONSOLE_READCONSOLE_CONTROL, CONSOLE_SCREEN_BUFFER_INFO,
CONSOLE_SCREEN_BUFFER_INFOEX, CONSOLE_SELECTION_INFO, DISABLE_NEWLINE_AUTO_RETURN,
ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS, ENABLE_INSERT_MODE, ENABLE_LINE_INPUT,
ENABLE_LVB_GRID_WORLDWIDE, ENABLE_MOUSE_INPUT, ENABLE_PROCESSED_INPUT,
ENABLE_PROCESSED_OUTPUT, ENABLE_QUICK_EDIT_MODE, ENABLE_VIRTUAL_TERMINAL_INPUT,
ENABLE_VIRTUAL_TERMINAL_PROCESSING, ENABLE_WINDOW_INPUT,
ENABLE_WRAP_AT_EOL_OUTPUT,
};
#[derive(Debug)]
#[repr(transparent)]
pub struct ConsoleScreenBuffer {
nn_handle: NonNull<winapi::ctypes::c_void>,
}
#[cfg(all(feature = "processenv", feature = "processthreadsapi"))]
impl core::clone::Clone for ConsoleScreenBuffer {
fn clone(&self) -> Self {
let mut dup: HANDLE = null_mut();
let b = unsafe {
DuplicateHandle(
GetCurrentProcess(),
self.nn_handle.as_ptr(),
GetCurrentProcess(),
&mut dup,
0,
TRUE,
DUPLICATE_SAME_ACCESS,
)
};
if b != 0 {
match NonNull::new(dup) {
Some(nn_handle) => ConsoleScreenBuffer { nn_handle },
None => panic!("Duplicated handle returned as null."),
}
} else {
panic!("Could not duplicate the handle.")
}
}
}
impl Drop for ConsoleScreenBuffer {
#[allow(clippy::if_same_then_else)]
fn drop(&mut self) {
if unsafe { CloseHandle(self.nn_handle.as_ptr()) } != 0 {
} else if cfg!(debug_assertions) {
}
}
}
impl ConsoleScreenBuffer {
pub fn new() -> Result<Self, ErrorCode> {
let security_attributes = SECURITY_ATTRIBUTES {
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: null_mut(),
bInheritHandle: TRUE,
};
let handle = unsafe {
CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&security_attributes,
CONSOLE_TEXTMODE_BUFFER,
null_mut(),
)
};
if handle != INVALID_HANDLE_VALUE {
Ok(Self {
nn_handle: NonNull::new(handle).unwrap(),
})
} else {
Err(get_last_error())
}
}
#[cfg(all(feature = "processenv", feature = "processthreadsapi"))]
pub fn from_stdout() -> Result<Self, ErrorCode> {
get_std_handle(StdHandle::Output).and_then(|std_handle| {
if std_handle != INVALID_HANDLE_VALUE && !std_handle.is_null() {
let mut std_dup: HANDLE = null_mut();
let b = unsafe {
DuplicateHandle(
GetCurrentProcess(),
std_handle,
GetCurrentProcess(),
&mut std_dup,
0,
TRUE,
DUPLICATE_SAME_ACCESS,
)
};
if b != 0 {
match NonNull::new(std_dup) {
Some(nn_handle) => Ok(ConsoleScreenBuffer { nn_handle }),
None => Err(ErrorCode(ErrorCode::APPLICATION_ERROR_BIT | 1)),
}
} else {
Err(get_last_error())
}
} else {
Err(ErrorCode(ErrorCode::APPLICATION_ERROR_BIT))
}
})
}
}
#[inline]
pub fn add_console_alias(
source: &str,
opt_target: Option<&str>,
exe: &str,
) -> Result<(), ErrorCode> {
let mut source_w = wide_null(source);
let mut exe_w = wide_null(exe);
let mut opt_target_w = opt_target.map(wide_null);
if unsafe {
AddConsoleAliasW(
source_w.as_mut_ptr(),
opt_target_w
.as_mut()
.map(|v| v.as_mut_ptr())
.unwrap_or(null_mut()),
exe_w.as_mut_ptr(),
)
} != 0
{
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub fn attach_console(process_id: u32) -> Result<(), ErrorCode> {
if unsafe { AttachConsole(process_id) } == 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn create_console_screen_buffer(
desired_access: u32,
share_mode: u32,
security_attributes: Option<&SECURITY_ATTRIBUTES>,
) -> Result<HANDLE, ErrorCode> {
let handle = CreateConsoleScreenBuffer(
desired_access,
share_mode,
opt_to_ptr(security_attributes),
CONSOLE_TEXTMODE_BUFFER,
null_mut(),
);
if handle != INVALID_HANDLE_VALUE {
Ok(handle)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn fill_console_output_attribute(
console_output: HANDLE,
attribute: u16,
length: u32,
write_coord: COORD,
) -> Result<u32, ErrorCode> {
let mut out: u32 = 0;
let s = FillConsoleOutputAttribute(
console_output,
attribute,
length,
write_coord,
&mut out,
);
if s != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn fill_console_output_character(
console_output: HANDLE,
ch: u16,
length: u32,
write_coord: COORD,
) -> Result<u32, ErrorCode> {
let mut out: u32 = 0;
let s =
FillConsoleOutputCharacterW(console_output, ch, length, write_coord, &mut out);
if s != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn flush_console_input_buffer(
console_input: HANDLE,
) -> Result<(), ErrorCode> {
if FlushConsoleInputBuffer(console_input) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub fn free_console() -> Result<(), ErrorCode> {
if unsafe { FreeConsole() } != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn generate_console_ctrl_event(
event: u32,
process_group_id: u32,
) -> Result<(), ErrorCode> {
if GenerateConsoleCtrlEvent(event, process_group_id) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_console_alias(
source: &str,
exe: &str,
) -> Result<String, ErrorCode> {
let mut source_w = wide_null(source);
let mut exe_w = wide_null(exe);
let mut buffer: Vec<u16> = vec![0; 4096];
if GetConsoleAliasW(
source_w.as_mut_ptr(),
buffer.as_mut_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
exe_w.as_mut_ptr(),
) != 0
{
let null_position = buffer.iter().copied().position(|u| u == 0).unwrap();
buffer.set_len(null_position);
Ok(String::from_utf16_lossy(&buffer))
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_aliases(exe: &str) -> Result<String, ErrorCode> {
let mut exe_w = wide_null(exe);
let needed_capacity =
unsafe { GetConsoleAliasesLengthW(exe_w.as_mut_ptr()) as usize };
let mut buffer: Vec<u16> = vec![0; needed_capacity];
if unsafe {
GetConsoleAliasesW(buffer.as_mut_ptr(), buffer.len() as u32, exe_w.as_mut_ptr())
} != 0
{
Ok(String::from_utf16_lossy(&buffer))
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_aliases_length(exe: &str) -> usize {
let mut exe_w = wide_null(exe);
unsafe { GetConsoleAliasesLengthW(exe_w.as_mut_ptr()) as usize }
}
#[inline]
pub unsafe fn get_console_cursor_info(
console_output: HANDLE,
) -> Result<CONSOLE_CURSOR_INFO, ErrorCode> {
let mut info = CONSOLE_CURSOR_INFO::default();
if GetConsoleCursorInfo(console_output, &mut info) != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_display_mode() -> Result<u32, ErrorCode> {
let mut out: u32 = 0;
if unsafe { GetConsoleDisplayMode(&mut out) } != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_console_font_size(
console_output: HANDLE,
font: u32,
) -> Result<COORD, ErrorCode> {
let c = GetConsoleFontSize(console_output, font);
if c.X != 0 && c.Y != 0 {
Ok(c)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_history_info() -> Result<CONSOLE_HISTORY_INFO, ErrorCode> {
let mut out = CONSOLE_HISTORY_INFO::default();
out.cbSize = size_of::<CONSOLE_HISTORY_INFO>() as u32;
if unsafe { GetConsoleHistoryInfo(&mut out) } != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
pub fn get_console_original_title() -> Result<String, ErrorCode> {
let mut buffer: Vec<u16> = Vec::with_capacity(1024);
loop {
let str_len_copied: u32 = unsafe {
GetConsoleOriginalTitleW(buffer.as_mut_ptr(), buffer.capacity() as u32)
};
if str_len_copied > 0 {
unsafe { buffer.set_len(usize::try_from(str_len_copied).unwrap()) };
return Ok(String::from_utf16_lossy(&buffer));
} else {
let e = get_last_error();
if e.0 == ERROR_SUCCESS {
let new_cap_target = buffer.capacity() * 2;
if new_cap_target > core::u32::MAX as usize {
return Err(e);
} else {
buffer.reserve(new_cap_target);
continue;
}
} else {
return Err(e);
}
}
}
}
#[inline]
pub fn get_console_process_list() -> Result<Vec<u32>, ErrorCode> {
let mut buffer: Vec<u32> = Vec::with_capacity(10);
loop {
let out = unsafe {
GetConsoleProcessList(buffer.as_mut_ptr(), buffer.capacity() as u32)
};
if out == 0 {
return Err(get_last_error());
}
if out <= buffer.capacity() as u32 {
unsafe { buffer.set_len(out as usize) };
return Ok(buffer);
} else {
buffer.reserve(out as usize);
continue;
}
}
}
#[inline]
pub unsafe fn get_console_screenbuffer_info(
console_output: HANDLE,
) -> Result<CONSOLE_SCREEN_BUFFER_INFO, ErrorCode> {
let mut info = CONSOLE_SCREEN_BUFFER_INFO::default();
if GetConsoleScreenBufferInfo(console_output, &mut info) != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_console_screenbuffer_info_ex(
console_output: HANDLE,
) -> Result<CONSOLE_SCREEN_BUFFER_INFOEX, ErrorCode> {
let mut info = CONSOLE_SCREEN_BUFFER_INFOEX::default();
info.cbSize = size_of::<CONSOLE_SCREEN_BUFFER_INFOEX>() as u32;
if GetConsoleScreenBufferInfoEx(console_output, &mut info) != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_selection_info() -> Result<CONSOLE_SELECTION_INFO, ErrorCode> {
let mut info = CONSOLE_SELECTION_INFO::default();
if unsafe { GetConsoleSelectionInfo(&mut info) } != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_console_title() -> Result<String, ErrorCode> {
let mut buffer: Vec<u16> = Vec::with_capacity(4096);
let out =
unsafe { GetConsoleTitleW(buffer.as_mut_ptr(), buffer.capacity() as u32) };
if out != 0 {
unsafe { buffer.set_len(out as usize - 1) };
Ok(String::from_utf16_lossy(&buffer))
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_console_window() -> HWND {
GetConsoleWindow()
}
#[inline]
pub unsafe fn get_current_console_font(
console_output: HANDLE,
max_window: bool,
) -> Result<CONSOLE_FONT_INFO, ErrorCode> {
let mut info = CONSOLE_FONT_INFO::default();
if GetCurrentConsoleFont(console_output, max_window as i32, &mut info) != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_current_console_font_ex(
console_output: HANDLE,
max_window: bool,
) -> Result<CONSOLE_FONT_INFOEX, ErrorCode> {
let mut info = CONSOLE_FONT_INFOEX::default();
info.cbSize = size_of::<CONSOLE_FONT_INFOEX>() as u32;
if GetCurrentConsoleFontEx(console_output, max_window as i32, &mut info) != 0 {
Ok(info)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn get_largest_console_window_size(
console_output: HANDLE,
) -> Result<COORD, ErrorCode> {
let coord = GetLargestConsoleWindowSize(console_output);
if coord.X != 0 && coord.Y != 0 {
Ok(coord)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn get_number_of_console_mouse_buttons() -> Result<u32, ErrorCode> {
let mut out: u32 = 0;
if unsafe { GetNumberOfConsoleMouseButtons(&mut out) } != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn peek_console_input(
console_input: HANDLE,
buffer: &mut [INPUT_RECORD],
) -> Result<usize, ErrorCode> {
let mut num_read: u32 = 0;
if PeekConsoleInputW(
console_input,
buffer.as_mut_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
&mut num_read,
) != 0
{
Ok(num_read as usize)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn read_console_output(
console_output: HANDLE,
buffer: &mut [CHAR_INFO],
buffer_size: COORD,
buffer_target_coord: COORD,
mut read_region: SMALL_RECT,
) -> Result<SMALL_RECT, ErrorCode> {
if ReadConsoleOutputW(
console_output,
buffer.as_mut_ptr(),
buffer_size,
buffer_target_coord,
&mut read_region,
) != 0
{
Ok(read_region)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn read_console_output_attribute(
console_output: HANDLE,
buffer: &mut [u16],
read_coord: COORD,
) -> Result<usize, ErrorCode> {
let mut num_read: u32 = 0;
if ReadConsoleOutputAttribute(
console_output,
buffer.as_mut_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
read_coord,
&mut num_read,
) != 0
{
Ok(num_read as usize)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn read_console_output_character(
console_output: HANDLE,
buffer: &mut [u16],
read_coord: COORD,
) -> Result<usize, ErrorCode> {
let mut num_read: u32 = 0;
if ReadConsoleOutputCharacterW(
console_output,
buffer.as_mut_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
read_coord,
&mut num_read,
) != 0
{
Ok(num_read as usize)
} else {
Err(get_last_error())
}
}
pub unsafe fn scroll_console_screen_buffer(
console_output: HANDLE,
scroll_rectangle: SMALL_RECT,
clip_rect: Option<SMALL_RECT>,
destination_origin: COORD,
fill: CHAR_INFO,
) -> Result<(), ErrorCode> {
if ScrollConsoleScreenBufferW(
console_output,
&scroll_rectangle,
opt_to_ptr(clip_rect.as_ref()),
destination_origin,
&fill,
) != 0
{
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_active_screen_buffer(
console_output: HANDLE,
) -> Result<(), ErrorCode> {
if SetConsoleActiveScreenBuffer(console_output) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub fn set_console_cp(code_page: u32) -> Result<(), ErrorCode> {
if unsafe { SetConsoleCP(code_page) } != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_cursor_info(
console_output: HANDLE,
info: CONSOLE_CURSOR_INFO,
) -> Result<(), ErrorCode> {
if SetConsoleCursorInfo(console_output, &info) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
pub unsafe fn set_console_cursor_position(
console_output: HANDLE,
position: COORD,
) -> Result<(), ErrorCode> {
if SetConsoleCursorPosition(console_output, position) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_display_mode(
console_output: HANDLE,
flags: u32,
) -> Result<COORD, ErrorCode> {
let mut out = COORD::default();
if SetConsoleDisplayMode(console_output, flags, &mut out) != 0 {
Ok(out)
} else {
Err(get_last_error())
}
}
#[inline]
pub fn set_console_history_info(
mut info: CONSOLE_HISTORY_INFO,
) -> Result<(), ErrorCode> {
info.cbSize = size_of::<CONSOLE_HISTORY_INFO>() as u32;
if unsafe { SetConsoleHistoryInfo(&mut info) } != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub fn set_console_output_cp(code_page: u32) -> Result<(), ErrorCode> {
if unsafe { SetConsoleOutputCP(code_page) } != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_screen_buffer_info_ex(
console_output: HANDLE,
mut info: CONSOLE_SCREEN_BUFFER_INFOEX,
) -> Result<(), ErrorCode> {
info.cbSize = size_of::<CONSOLE_SCREEN_BUFFER_INFOEX>() as u32;
if SetConsoleScreenBufferInfoEx(console_output, &mut info) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_screen_buffer_size(
console_output: HANDLE,
size: COORD,
) -> Result<(), ErrorCode> {
if SetConsoleScreenBufferSize(console_output, size) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_text_attribute(
console_output: HANDLE,
attribute: u16,
) -> Result<(), ErrorCode> {
if SetConsoleTextAttribute(console_output, attribute) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub fn set_console_title(title: &str) -> Result<(), ErrorCode> {
let wide = wide_null(title);
if unsafe { SetConsoleTitleW(wide.as_ptr()) } != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_console_window_info(
console_output: HANDLE,
absolute: bool,
window: SMALL_RECT,
) -> Result<(), ErrorCode> {
if SetConsoleWindowInfo(console_output, absolute as i32, &window) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn set_current_console_font_ex(
console_output: HANDLE,
maximum: bool,
mut info: CONSOLE_FONT_INFOEX,
) -> Result<(), ErrorCode> {
info.cbSize = size_of::<CONSOLE_FONT_INFOEX>() as u32;
if SetCurrentConsoleFontEx(console_output, maximum as i32, &mut info) != 0 {
Ok(())
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn write_console_input(
console_input: HANDLE,
buffer: &[INPUT_RECORD],
) -> Result<usize, ErrorCode> {
let mut events_written: u32 = 0;
if WriteConsoleInputW(
console_input,
buffer.as_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
&mut events_written,
) != 0
{
Ok(events_written as usize)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn write_console_output(
console_output: HANDLE,
buffer: &[CHAR_INFO],
buffer_size: COORD,
buffer_coord: COORD,
mut write_region: SMALL_RECT,
) -> Result<SMALL_RECT, ErrorCode> {
if WriteConsoleOutputW(
console_output,
buffer.as_ptr(),
buffer_size,
buffer_coord,
&mut write_region,
) != 0
{
Ok(write_region)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn write_console_output_attribute(
console_output: HANDLE,
buffer: &[u16],
position: COORD,
) -> Result<usize, ErrorCode> {
let mut num_written: u32 = 0;
if WriteConsoleOutputAttribute(
console_output,
buffer.as_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
position,
&mut num_written,
) != 0
{
Ok(num_written as usize)
} else {
Err(get_last_error())
}
}
#[inline]
pub unsafe fn write_console_output_character(
console_output: HANDLE,
buffer: &[u16],
position: COORD,
) -> Result<usize, ErrorCode> {
let mut num_written: u32 = 0;
if WriteConsoleOutputCharacterW(
console_output,
buffer.as_ptr(),
buffer.len().min(core::u32::MAX as usize) as u32,
position,
&mut num_written,
) != 0
{
Ok(num_written as usize)
} else {
Err(get_last_error())
}
}