///|
/// Open the Windows console input device (`CONIN$`) as a synchronous,
/// non-overlapped handle. Returns an invalid handle on failure, with the
/// failure reason available through `GetLastError` (read by `@os_error`).
#cfg(platform="windows")
extern "C" fn open_console_input() -> @async/types.Fd = "moonbit_tty_open_console_input"
///|
/// Open the Windows console output device (`CONOUT$`) as a synchronous,
/// non-overlapped handle. Returns an invalid handle on failure, with the
/// failure reason available through `GetLastError` (read by `@os_error`).
#cfg(platform="windows")
extern "C" fn open_console_output() -> @async/types.Fd = "moonbit_tty_open_console_output"
///|
#cfg(platform="windows")
extern "C" fn console_handle_is_valid(fd : @async/types.Fd) -> Int = "moonbit_tty_handle_is_valid"
///|
/// Open the controlling terminal as one coordinated terminal handle.
///
/// `CONIN$` / `CONOUT$` are console (character) devices, not filesystem files,
/// so they are opened directly and wrapped as raw, non-overlapped handles via
/// `@async/raw_fd.RawFdStream`. `RawFdStream` detects the handle kind with
/// `GetFileType` (`CharDevice`) and never registers it for overlapped IO, which
/// is exactly what console devices need. Routing through `@async/fs.open`
/// instead would probe the handle with `GetFileInformationByHandle`, which fails
/// on console devices.
#cfg(platform="windows")
pub async fn Tty::open() -> Tty {
let input_fd = open_console_input()
if console_handle_is_valid(input_fd) == 0 {
@os_error.check_errno("Tty::open")
}
let input = @async/raw_fd.RawFdStream(input_fd)
let output_fd = open_console_output()
if console_handle_is_valid(output_fd) == 0 {
input.close()
@os_error.check_errno("Tty::open")
}
let output = @async/raw_fd.RawFdStream(output_fd) catch {
error => {
input.close()
raise error
}
}
Tty::new(input, output)
}