fenestroj 0.0.11

Easier wrappers for Win32 API stuff, safe when possible
Documentation
#![cfg(feature = "consoleapi")]

//! Functions to interact with a process's console.
//!
//! The stuff here extends and compliments the [`wincon`] module, which you
//! should also check out.

use super::*;

use winapi::um::consoleapi::{
  AllocConsole, ClosePseudoConsole, CreatePseudoConsole, GetConsoleCP,
  GetConsoleMode, GetConsoleOutputCP, GetNumberOfConsoleInputEvents,
  ReadConsoleInputW, ReadConsoleW, ResizePseudoConsole, SetConsoleCtrlHandler,
  SetConsoleMode, WriteConsoleW,
};

/// Allocates a new console for the process and attaches to it.
///
/// A process can only be attached to one console at a time. A "console"
/// process starts with one, but a "GUI" process does not.
///
/// If you try to allocate a console while your process is already attached to
/// one this will fail.
///
/// See
/// [`AllocConsole`](https://docs.microsoft.com/en-us/windows/console/AllocConsole)
#[inline]
pub fn alloc_console() -> Result<(), ErrorCode> {
  if unsafe { AllocConsole() } != 0 {
    Ok(())
  } else {
    Err(get_last_error())
  }
}

/// See [`ClosePseudoConsole`](https://docs.microsoft.com/en-us/windows/console/ClosePseudoConsole)
#[inline]
pub unsafe fn close_pseudo_console(hpc: HPCON) {
  ClosePseudoConsole(hpc)
}

/// See [`CreatePseudoConsole`](https://docs.microsoft.com/en-us/windows/console/CreatePseudoConsole)
#[inline]
pub unsafe fn create_pseudo_console(
  size: COORD,
  input: HANDLE,
  output: HANDLE,
  flags: u32,
) -> Result<HPCON, HRESULT> {
  let mut hpc = null_mut();
  let hresult = CreatePseudoConsole(size, input, output, flags, &mut hpc);
  if hresult == S_OK {
    Ok(hpc)
  } else {
    Err(hresult)
  }
}

/// Gets the input code page of the console.
///
/// You can set the console's input code page with [`set_console_cp`].
///
/// Note that the _output_ code page is controlled separately with
/// [`get_console_output_cp`] and [`set_console_output_cp`].
///
/// See [`GetConsoleCP`](https://docs.microsoft.com/en-us/windows/console/GetConsoleCP)
#[inline]
pub fn get_console_cp() -> Result<u32, ErrorCode> {
  let page = unsafe { GetConsoleCP() };
  if page != 0 {
    Ok(page)
  } else {
    Err(get_last_error())
  }
}

/// Obtains info about a console's mode
///
/// See [`GetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/getconsolemode)
#[inline]
pub unsafe fn get_console_mode(handle: HANDLE) -> Result<u32, ErrorCode> {
  let mut mode: u32 = 0;
  if GetConsoleMode(handle, &mut mode) != 0 {
    Ok(mode)
  } else {
    Err(get_last_error())
  }
}

/// Determines the output code page of the console.
///
/// You can change the output code page with [`set_console_output_cp`].
///
/// Note that the _input_ code page is controlled separately with
/// [`get_console_cp`] and [`set_console_cp`].
///
/// See
/// [`GetConsoleOutputCP`](https://docs.microsoft.com/en-us/windows/console/GetConsoleOutputCP)
#[inline]
pub fn get_console_output_cp() -> Result<u32, ErrorCode> {
  let out = unsafe { GetConsoleOutputCP() };
  if out != 0 {
    Ok(out)
  } else {
    Err(get_last_error())
  }
}

/// See [`GetNumberOfConsoleInputEvents`](https://docs.microsoft.com/en-us/windows/console/GetNumberOfConsoleInputEvents)
#[inline]
pub unsafe fn get_number_of_console_input_events(
  console_input: HANDLE,
) -> Result<u32, ErrorCode> {
  let mut out: u32 = 0;
  if GetNumberOfConsoleInputEvents(console_input, &mut out) != 0 {
    Ok(out)
  } else {
    Err(get_last_error())
  }
}

/// See [`ReadConsole`](https://docs.microsoft.com/en-us/windows/console/ReadConsole) (uses `W` version)
#[inline]
pub unsafe fn read_console(
  console_input: HANDLE,
  buffer: &mut [u16],
  mut opt_control: Option<CONSOLE_READCONSOLE_CONTROL>,
) -> Result<usize, ErrorCode> {
  let mut units_read: u32 = 0;
  let m: Option<&mut CONSOLE_READCONSOLE_CONTROL> = opt_control.as_mut();
  if ReadConsoleW(
    console_input,
    buffer.as_mut_ptr() as *mut _,
    buffer.len().min(core::u32::MAX as usize) as u32,
    &mut units_read,
    opt_to_mut_ptr(m),
  ) != 0
  {
    Ok(units_read as usize)
  } else {
    Err(get_last_error())
  }
}

/// See [`ReadConsoleInput`](https://docs.microsoft.com/en-us/windows/console/ReadConsoleInput) (uses the `W` version)
#[inline]
pub unsafe fn read_console_input(
  console_input: HANDLE,
  buffer: &mut [INPUT_RECORD],
) -> Result<usize, ErrorCode> {
  let mut num_read: u32 = 0;
  if ReadConsoleInputW(
    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())
  }
}

/// See [`ResizePseudoConsole`](https://docs.microsoft.com/en-us/windows/console/ResizePseudoConsole)
#[inline]
pub unsafe fn resize_pseudo_console(hpc: HPCON, size: COORD) -> Result<(), HRESULT> {
  let h = ResizePseudoConsole(hpc, size);
  if h == S_OK {
    Ok(())
  } else {
    Err(h)
  }
}

/// See [`SetConsoleCtrlHandler`](https://docs.microsoft.com/en-us/windows/console/SetConsoleCtrlHandler)
#[inline]
pub unsafe fn set_console_ctrl_handler(
  routine: Option<unsafe extern "system" fn(u32) -> i32>,
  add: bool,
) -> Result<(), ErrorCode> {
  if SetConsoleCtrlHandler(routine, add as i32) != 0 {
    Ok(())
  } else {
    Err(get_last_error())
  }
}

/// Sets a console mode configuration.
///
/// See [`SetConsoleMode`](https://docs.microsoft.com/en-us/windows/console/setconsolemode)
#[inline]
pub unsafe fn set_console_mode(handle: HANDLE, mode: u32) -> Result<(), ErrorCode> {
  if SetConsoleMode(handle, mode) != 0 {
    Ok(())
  } else {
    Err(get_last_error())
  }
}

/// Write a UTF-16 slice to the console.
///
/// * The slice does _not_ need to be null terminated.
/// * This can only write up to `u32::MAX` code units at once.
/// 
/// The return value is `Ok(code units written)` or an error code.
///
/// See
/// [`WriteConsole`](https://docs.microsoft.com/en-us/windows/console/WriteConsole)
/// (uses the `W` version)
#[inline]
pub fn write_console(
  console_output: HANDLE,
  message: &[u16],
) -> Result<usize, ErrorCode> {
  let mut units_written: u32 = 0;
  let write_status = unsafe {
    WriteConsoleW(
      console_output,
      message.as_ptr() as *const _,
      message.len().min(core::u32::MAX as usize) as u32,
      &mut units_written,
      null_mut(),
    )
  };
  if write_status != 0 {
    Ok(units_written as usize)
  } else {
    Err(get_last_error())
  }
}