Skip to content

Commit bd391b1

Browse files
committed
Windows: Borrow stdin/stdout when detected as a TTY
This matches the same code in the Unix terminal module and produces better behavior in some edge cases like running an application into a pipe like `hx | echo`.
1 parent 8ae1d58 commit bd391b1

File tree

1 file changed

+61
-18
lines changed

1 file changed

+61
-18
lines changed

src/terminal/windows.rs

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
2-
fs,
3-
io::{self, BufWriter, Write as _},
2+
fs::{self, File},
3+
io::{self, BufWriter, IsTerminal as _, Write as _},
44
mem,
55
os::windows::prelude::*,
66
ptr,
@@ -45,14 +45,55 @@ const CP_UTF8: CodePageID = 65001;
4545
// This crate however uses `windows-sys` instead of `winapi` and has a slightly different API for
4646
// the `InputHandle` and `OutputHandle`.
4747

48+
#[derive(Debug)]
49+
pub enum Handle {
50+
Owned(OwnedHandle),
51+
Borrowed(BorrowedHandle<'static>),
52+
}
53+
54+
impl AsRawHandle for Handle {
55+
fn as_raw_handle(&self) -> RawHandle {
56+
match self {
57+
Self::Owned(handle) => handle.as_raw_handle(),
58+
Self::Borrowed(handle) => handle.as_raw_handle(),
59+
}
60+
}
61+
}
62+
63+
impl Handle {
64+
pub fn stdin() -> Self {
65+
let stdin = io::stdin().as_raw_handle();
66+
Self::Borrowed(unsafe { BorrowedHandle::borrow_raw(stdin) })
67+
}
68+
69+
pub fn stdout() -> Self {
70+
let stdout = io::stdout().as_raw_handle();
71+
Self::Borrowed(unsafe { BorrowedHandle::borrow_raw(stdout) })
72+
}
73+
74+
pub fn try_clone(&self) -> io::Result<Self> {
75+
let this = match self {
76+
Self::Owned(handle) => Self::Owned(handle.try_clone()?),
77+
Self::Borrowed(handle) => Self::Borrowed(*handle),
78+
};
79+
Ok(this)
80+
}
81+
}
82+
83+
impl From<File> for Handle {
84+
fn from(file: File) -> Self {
85+
Self::Owned(OwnedHandle::from(file))
86+
}
87+
}
88+
4889
#[derive(Debug)]
4990
pub(crate) struct InputHandle {
50-
handle: OwnedHandle,
91+
handle: Handle,
5192
}
5293

5394
impl InputHandle {
54-
fn new<H: Into<OwnedHandle>>(h: H) -> Self {
55-
Self { handle: h.into() }
95+
fn new(handle: Handle) -> Self {
96+
Self { handle }
5697
}
5798

5899
fn try_clone(&self) -> io::Result<Self> {
@@ -151,12 +192,12 @@ impl AsRawHandle for InputHandle {
151192

152193
#[derive(Debug)]
153194
pub struct OutputHandle {
154-
handle: OwnedHandle,
195+
handle: Handle,
155196
}
156197

157198
impl OutputHandle {
158-
fn new<H: Into<OwnedHandle>>(h: H) -> Self {
159-
Self { handle: h.into() }
199+
fn new(handle: Handle) -> Self {
200+
Self { handle }
160201
}
161202

162203
fn get_mode(&self) -> io::Result<CONSOLE_MODE> {
@@ -248,20 +289,22 @@ impl io::Write for OutputHandle {
248289
}
249290

250291
fn open_pty() -> io::Result<(InputHandle, OutputHandle)> {
251-
// TODO: attempt to handle stdin/stdout?
252-
let input = InputHandle::new(
253-
fs::OpenOptions::new()
292+
let (input, output) = if io::stdin().is_terminal() {
293+
(Handle::stdin(), Handle::stdout())
294+
} else {
295+
let input = fs::OpenOptions::new()
254296
.read(true)
255297
.write(true)
256-
.open("CONIN$")?,
257-
);
258-
let output = OutputHandle::new(
259-
fs::OpenOptions::new()
298+
.open("CONIN$")?
299+
.into();
300+
let output = fs::OpenOptions::new()
260301
.read(true)
261302
.write(true)
262-
.open("CONOUT$")?,
263-
);
264-
Ok((input, output))
303+
.open("CONOUT$")?
304+
.into();
305+
(input, output)
306+
};
307+
Ok((InputHandle::new(input), OutputHandle::new(output)))
265308
}
266309

267310
// CREDIT: Again, like the UnixTerminal in the unix module this is mostly based on WezTerm but

0 commit comments

Comments
 (0)