|
|
|
@ -1,37 +1,39 @@
|
|
|
|
|
mod error;
|
|
|
|
|
mod log;
|
|
|
|
|
|
|
|
|
|
use std::ffi::OsString;
|
|
|
|
|
use windows::Win32::{Foundation::HANDLE, System::Console};
|
|
|
|
|
use windows::{h, s, w};
|
|
|
|
|
|
|
|
|
|
use anyhow::{Context, Result};
|
|
|
|
|
|
|
|
|
|
use crate::{error::Error, log::*};
|
|
|
|
|
|
|
|
|
|
fn print_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
fn log_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// Characters read by the ReadFile or ReadConsole function are written to the active screen
|
|
|
|
|
// buffer as they are typed into the console. This mode can be used only if the
|
|
|
|
|
// ENABLE_LINE_INPUT mode is also enabled.
|
|
|
|
|
if (mode & Console::ENABLE_ECHO_INPUT).0 > 0 {
|
|
|
|
|
println!("Echo Input: Enabled");
|
|
|
|
|
debug!("Echo Input: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Echo Input: Disabled");
|
|
|
|
|
debug!("Echo Input: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When enabled, text entered in a console window will be inserted at the current cursor
|
|
|
|
|
// location and all text following that location will not be overwritten. When disabled, all
|
|
|
|
|
// following text will be overwritten.
|
|
|
|
|
if (mode & Console::ENABLE_INSERT_MODE).0 > 0 {
|
|
|
|
|
println!("Insert Mode: Enabled");
|
|
|
|
|
debug!("Insert Mode: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Insert Mode: Disabled");
|
|
|
|
|
debug!("Insert Mode: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The ReadFile or ReadConsole function returns only when a carriage return character is read.
|
|
|
|
|
// If this mode is disabled, the functions return when one or more characters are available.
|
|
|
|
|
if (mode & Console::ENABLE_LINE_INPUT).0 > 0 {
|
|
|
|
|
println!("Line Input Mode: Enabled");
|
|
|
|
|
debug!("Line Input Mode: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Line Input Mode: Disabled");
|
|
|
|
|
debug!("Line Input Mode: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the mouse pointer is within the borders of the console window and the window has the
|
|
|
|
@ -40,9 +42,9 @@ fn print_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// is enabled. The ReadConsoleInput function can be used to read MOUSE_EVENT input records from
|
|
|
|
|
// the input buffer.
|
|
|
|
|
if (mode & Console::ENABLE_MOUSE_INPUT).0 > 0 {
|
|
|
|
|
println!("Mouse Input: Enabled");
|
|
|
|
|
debug!("Mouse Input: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Mouse Input: Disabled");
|
|
|
|
|
debug!("Mouse Input: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CTRL+C is processed by the system and is not placed in the input buffer. If the input buffer
|
|
|
|
@ -51,18 +53,18 @@ fn print_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// also enabled, backspace, carriage return, and line feed characters are handled by the
|
|
|
|
|
// system.
|
|
|
|
|
if (mode & Console::ENABLE_PROCESSED_INPUT).0 > 0 {
|
|
|
|
|
println!("Processed Input: Enabled");
|
|
|
|
|
debug!("Processed Input: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Processed Input: Disabled");
|
|
|
|
|
debug!("Processed Input: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This flag enables the user to use the mouse to select and edit text. To enable this mode,
|
|
|
|
|
// use ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS. To disable this mode, use
|
|
|
|
|
// ENABLE_EXTENDED_FLAGS without this flag.
|
|
|
|
|
if (mode & Console::ENABLE_QUICK_EDIT_MODE).0 > 0 {
|
|
|
|
|
println!("Quick Edit Mode: Enabled");
|
|
|
|
|
debug!("Quick Edit Mode: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Quick Edit Mode: Disabled");
|
|
|
|
|
debug!("Quick Edit Mode: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// User interactions that change the size of the console screen buffer are reported in the
|
|
|
|
@ -70,9 +72,9 @@ fn print_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// applications using the ReadConsoleInput function, but not by those using ReadFile or
|
|
|
|
|
// ReadConsole.
|
|
|
|
|
if (mode & Console::ENABLE_WINDOW_INPUT).0 > 0 {
|
|
|
|
|
println!("Window Input: Enabled");
|
|
|
|
|
debug!("Window Input: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Window Input: Disabled");
|
|
|
|
|
debug!("Window Input: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setting this flag directs the Virtual Terminal processing engine to convert user input
|
|
|
|
@ -83,22 +85,22 @@ fn print_input_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// ENABLE_VIRTUAL_TERMINAL_PROCESSING on the output handle to connect to an application that
|
|
|
|
|
// communicates exclusively via virtual terminal sequences.
|
|
|
|
|
if (mode & Console::ENABLE_VIRTUAL_TERMINAL_INPUT).0 > 0 {
|
|
|
|
|
println!("Virtual Terminal Input: Enabled");
|
|
|
|
|
debug!("Virtual Terminal Input: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Virtual Terminal Input: Disabled");
|
|
|
|
|
debug!("Virtual Terminal Input: Disabled");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn print_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
fn log_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// Characters written by the WriteFile or WriteConsole function or echoed by the ReadFile or
|
|
|
|
|
// ReadConsole function are parsed for ASCII control sequences, and the correct action is
|
|
|
|
|
// performed. Backspace, tab, bell, carriage return, and line feed characters are processed. It
|
|
|
|
|
// should be enabled when using control sequences or when ENABLE_VIRTUAL_TERMINAL_PROCESSING is
|
|
|
|
|
// set.
|
|
|
|
|
if (mode & Console::ENABLE_PROCESSED_OUTPUT).0 > 0 {
|
|
|
|
|
println!("Processed Output: Enabled");
|
|
|
|
|
debug!("Processed Output: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Processed Output: Disabled");
|
|
|
|
|
debug!("Processed Output: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When writing with WriteFile or WriteConsole or echoing with ReadFile or ReadConsole, the
|
|
|
|
@ -109,9 +111,9 @@ fn print_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// when the cursor advances beyond the last row in the console screen buffer. If this mode is
|
|
|
|
|
// disabled, the last character in the row is overwritten with any subsequent characters.
|
|
|
|
|
if (mode & Console::ENABLE_WRAP_AT_EOL_OUTPUT).0 > 0 {
|
|
|
|
|
println!("Wrap at EOL: Enabled");
|
|
|
|
|
debug!("Wrap at EOL: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Wrap at EOL: Disabled");
|
|
|
|
|
debug!("Wrap at EOL: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When writing with WriteFile or WriteConsole, characters are parsed for VT100 and similar
|
|
|
|
@ -121,9 +123,9 @@ fn print_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
//
|
|
|
|
|
// Ensure ENABLE_PROCESSED_OUTPUT is set when using this flag.
|
|
|
|
|
if (mode & Console::ENABLE_VIRTUAL_TERMINAL_PROCESSING).0 > 0 {
|
|
|
|
|
println!("Terminal Processing: Enabled");
|
|
|
|
|
debug!("Terminal Processing: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Terminal Processing: Disabled");
|
|
|
|
|
debug!("Terminal Processing: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When writing with WriteFile or WriteConsole, this adds an additional state to end-of-line
|
|
|
|
@ -144,9 +146,9 @@ fn print_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// final character on the screen (../in the bottom right corner) without triggering an
|
|
|
|
|
// immediate scroll is the desired behavior.
|
|
|
|
|
if (mode & Console::DISABLE_NEWLINE_AUTO_RETURN).0 > 0 {
|
|
|
|
|
println!("Newline Auto Return: Enabled");
|
|
|
|
|
debug!("Newline Auto Return: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("Newline Auto Return: Disabled");
|
|
|
|
|
debug!("Newline Auto Return: Disabled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The APIs for writing character attributes including WriteConsoleOutput and
|
|
|
|
@ -170,9 +172,9 @@ fn print_output_mode(mode: Console::CONSOLE_MODE) {
|
|
|
|
|
// reverse video flags being set while this flag is still off if the attached application
|
|
|
|
|
// requests underlining or inverse video via Console Virtual Terminal Sequences.
|
|
|
|
|
if (mode & Console::ENABLE_LVB_GRID_WORLDWIDE).0 > 0 {
|
|
|
|
|
println!("LVB Grid: Enabled");
|
|
|
|
|
debug!("LVB Grid: Enabled");
|
|
|
|
|
} else {
|
|
|
|
|
println!("LVB Grid: Disabled");
|
|
|
|
|
debug!("LVB Grid: Disabled");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -215,19 +217,23 @@ fn setup_stdin() -> Result<()> {
|
|
|
|
|
|
|
|
|
|
Error::check(Console::SetConsoleMode(handle, mode))?;
|
|
|
|
|
Error::check(Console::GetConsoleMode(handle, &mut mode))?;
|
|
|
|
|
println!("Stdin details:");
|
|
|
|
|
print_input_mode(mode);
|
|
|
|
|
debug!("Stdin details:");
|
|
|
|
|
log_input_mode(mode);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn setup_stdout() -> Result<()> {
|
|
|
|
|
unsafe {
|
|
|
|
|
Console::SetConsoleOutputCP(65001);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut mode = Console::CONSOLE_MODE(0);
|
|
|
|
|
unsafe {
|
|
|
|
|
let handle = stdout_handle()?;
|
|
|
|
|
if Console::GetConsoleMode(handle, &mut mode).as_bool() {
|
|
|
|
|
println!("Stdout details:");
|
|
|
|
|
print_output_mode(mode);
|
|
|
|
|
debug!("Stdout details:");
|
|
|
|
|
log_output_mode(mode);
|
|
|
|
|
} else {
|
|
|
|
|
return Err(Error::last_error().into());
|
|
|
|
|
}
|
|
|
|
@ -241,23 +247,24 @@ fn main() -> Result<()> {
|
|
|
|
|
setup_stdout()?;
|
|
|
|
|
|
|
|
|
|
let stdin = stdin_handle()?;
|
|
|
|
|
let stdout = stdout_handle()?;
|
|
|
|
|
let mut buf = [Console::INPUT_RECORD::default(); 100];
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let mut n: u32 = 0;
|
|
|
|
|
unsafe {
|
|
|
|
|
Error::check(Console::ReadConsoleInputW(stdin, &mut buf, &mut n))?;
|
|
|
|
|
}
|
|
|
|
|
let n = n as usize;
|
|
|
|
|
println!("read: {:?}", n);
|
|
|
|
|
for rec in &buf[0..n] {
|
|
|
|
|
println!("Event Type: {}", rec.EventType);
|
|
|
|
|
debug!("Event Type: {}", rec.EventType);
|
|
|
|
|
match rec.EventType as u32 {
|
|
|
|
|
Console::FOCUS_EVENT => {
|
|
|
|
|
// The Event member contains a FOCUS_EVENT_RECORD structure. These events are
|
|
|
|
|
// used internally and should be ignored.
|
|
|
|
|
unsafe {
|
|
|
|
|
let event = rec.Event.FocusEvent;
|
|
|
|
|
println!("Focus Event: {:?}", event);
|
|
|
|
|
debug!("Focus Event: {:?}", event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Console::MENU_EVENT => {
|
|
|
|
@ -265,7 +272,7 @@ fn main() -> Result<()> {
|
|
|
|
|
// used internally and should be ignored.
|
|
|
|
|
unsafe {
|
|
|
|
|
let event = rec.Event.MenuEvent;
|
|
|
|
|
println!("Menu Event: {:?}", event);
|
|
|
|
|
debug!("Menu Event: {:?}", event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Console::KEY_EVENT => {
|
|
|
|
@ -273,13 +280,72 @@ fn main() -> Result<()> {
|
|
|
|
|
// about a keyboard event.
|
|
|
|
|
unsafe {
|
|
|
|
|
let event = rec.Event.KeyEvent;
|
|
|
|
|
let down = event.bKeyDown;
|
|
|
|
|
let down = event.bKeyDown.as_bool();
|
|
|
|
|
let repeats = event.wRepeatCount;
|
|
|
|
|
let key_code = event.wVirtualKeyCode;
|
|
|
|
|
let scan_code = event.wVirtualScanCode;
|
|
|
|
|
let c = event.uChar.UnicodeChar;
|
|
|
|
|
let modifiers = event.dwControlKeyState;
|
|
|
|
|
println!(
|
|
|
|
|
// handle key presses on key up, not key down. This is a little weird but
|
|
|
|
|
// it's sending both down and up events when you hit the key anyway. I
|
|
|
|
|
// dunno if this is right. But for some reason, the down events don't have
|
|
|
|
|
// the modifiers
|
|
|
|
|
if !down {
|
|
|
|
|
// Carriage Return (Enter key)
|
|
|
|
|
if c == 13 {
|
|
|
|
|
let mask = Console::LEFT_ALT_PRESSED
|
|
|
|
|
| Console::RIGHT_ALT_PRESSED
|
|
|
|
|
| Console::LEFT_CTRL_PRESSED
|
|
|
|
|
| Console::RIGHT_CTRL_PRESSED
|
|
|
|
|
| Console::SHIFT_PRESSED;
|
|
|
|
|
if modifiers & mask == 0 {
|
|
|
|
|
Error::check(Console::WriteConsoleA(
|
|
|
|
|
stdout,
|
|
|
|
|
"\r\n".as_bytes(),
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
))?;
|
|
|
|
|
}
|
|
|
|
|
} else if key_code == 74 && modifiers == 40 {
|
|
|
|
|
// key j modifier ctrl
|
|
|
|
|
// let text = "X";
|
|
|
|
|
let text = "•";
|
|
|
|
|
// let z = text.encode_utf16().map(|c| c.to_ne_bytes());
|
|
|
|
|
//let t16: Vec<u16> = text
|
|
|
|
|
// .encode_utf16()
|
|
|
|
|
// .chain(std::iter::once(0 as u16))
|
|
|
|
|
// .collect();
|
|
|
|
|
// t16.as_ptr()
|
|
|
|
|
// let (_, k, _) = t16.align_to::<u8>();
|
|
|
|
|
// let x: &[u8] = &[0x20, 0x22, 0x00, 0x00];
|
|
|
|
|
//let x: &[u16] = &[0x2022];
|
|
|
|
|
//let mut z = x[0].to_ne_bytes();
|
|
|
|
|
//let z: [u8] = [(0x2022 as u16).to_ne_bytes()];
|
|
|
|
|
//let mut z = Vec<u8>::new();
|
|
|
|
|
//text.as_bytes()
|
|
|
|
|
|
|
|
|
|
Error::check(Console::WriteConsoleA(
|
|
|
|
|
stdout,
|
|
|
|
|
text.as_bytes(),
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
))?;
|
|
|
|
|
} else {
|
|
|
|
|
if let Some(c) = char::from_u32(event.uChar.UnicodeChar as u32) {
|
|
|
|
|
if !c.is_control() {
|
|
|
|
|
let mut buf = [0 as u8; 2];
|
|
|
|
|
let s = c.encode_utf8(&mut buf);
|
|
|
|
|
Error::check(Console::WriteConsoleW(
|
|
|
|
|
stdout,
|
|
|
|
|
s.as_bytes(),
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
))?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
debug!(
|
|
|
|
|
"Key Event: down: {down:?} repeats: {repeats} key-code: {key_code} scan-code: {scan_code} char: {c} modifiers: {modifiers}"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
@ -289,7 +355,7 @@ fn main() -> Result<()> {
|
|
|
|
|
// about a mouse movement or button press event.
|
|
|
|
|
unsafe {
|
|
|
|
|
let event = rec.Event.MouseEvent;
|
|
|
|
|
println!("Mouse Event: {:?}", event);
|
|
|
|
|
debug!("Mouse Event: {:?}", event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Console::WINDOW_BUFFER_SIZE_EVENT => {
|
|
|
|
@ -297,7 +363,7 @@ fn main() -> Result<()> {
|
|
|
|
|
// information about the new size of the console screen buffer.
|
|
|
|
|
unsafe {
|
|
|
|
|
let event = rec.Event.WindowBufferSizeEvent;
|
|
|
|
|
println!("Window Buffer Event: {:?}", event);
|
|
|
|
|
debug!("Window Buffer Event: {:?}", event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|