1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use cfg_if::cfg_if;
31use std::io;
32use std::marker::PhantomData;
33use std::sync::{Mutex, MutexGuard};
34
35cfg_if! {
36 if #[cfg(all(unix, not(terminal_trx_test_unsupported)))] {
37 mod unix;
38 use unix as imp;
39 } else if #[cfg(all(windows, not(terminal_trx_test_unsupported)))] {
40 mod windows;
41 use windows as imp;
42 } else {
43 mod unsupported;
44 use unsupported as imp;
45 }
46}
47
48#[doc = include_str!("../readme.md")]
49#[cfg(doctest)]
50pub mod readme_doctests {}
51
52static TERMINAL_LOCK: Mutex<()> = Mutex::new(());
53
54pub fn terminal() -> io::Result<Terminal> {
72 imp::terminal().map(Terminal)
73}
74
75macro_rules! impl_transceive {
76 ($($extra_supertraits:tt)*) => {
77 pub trait Transceive: io::Read + io::Write $($extra_supertraits)* + sealed::Sealed {}
79 };
80}
81
82cfg_if! {
83 if #[cfg(terminal_trx_test_unsupported)] {
84 impl_transceive! { }
85 } else if #[cfg(unix)] {
86 impl_transceive! { + std::os::fd::AsFd + std::os::fd::AsRawFd }
87 } else if #[cfg(windows)] {
88 impl_transceive! { + ConsoleHandles }
89 } else {
90 impl_transceive! { }
91 }
92}
93
94#[cfg(all(windows, not(terminal_trx_test_unsupported)))]
96#[cfg_attr(docsrs, doc(cfg(windows)))]
97pub trait ConsoleHandles {
98 fn input_buffer_handle(&self) -> std::os::windows::io::BorrowedHandle<'_>;
100
101 fn screen_buffer_handle(&self) -> std::os::windows::io::BorrowedHandle<'_>;
103}
104
105mod sealed {
106 pub trait Sealed {}
107}
108
109#[derive(Debug)]
114pub struct Terminal(imp::Terminal);
115
116#[cfg(test)]
117static_assertions::assert_impl_all!(Terminal: Send, Sync, std::panic::UnwindSafe, std::panic::RefUnwindSafe);
118
119impl sealed::Sealed for Terminal {}
120impl Transceive for Terminal {}
121
122impl io::Read for Terminal {
123 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
124 self.lock().read(buf)
125 }
126}
127
128impl io::Write for Terminal {
129 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
130 self.lock().write(buf)
131 }
132
133 fn flush(&mut self) -> io::Result<()> {
134 self.lock().flush()
135 }
136}
137
138impl Terminal {
139 pub fn lock(&mut self) -> TerminalLock<'_> {
144 let mutex_guard = TERMINAL_LOCK.lock().unwrap_or_else(|e| e.into_inner());
145 let stdio_locks = self.0.lock_stdio();
146 TerminalLock {
147 inner: &mut self.0,
148 _stdio_locks: stdio_locks,
149 _mutex_guard: mutex_guard,
150 _phantom_data: PhantomData,
151 }
152 }
153
154 pub fn has_connected_stdio_stream(&self) -> bool {
156 self.0.has_connected_stdio_stream()
157 }
158}
159
160#[derive(Debug)]
163pub struct TerminalLock<'a> {
164 inner: &'a mut imp::Terminal,
165 _mutex_guard: MutexGuard<'static, ()>,
166 _stdio_locks: StdioLocks,
167 _phantom_data: PhantomData<*mut ()>,
168}
169
170#[cfg(test)]
171static_assertions::assert_not_impl_any!(TerminalLock<'_>: Send, Sync);
172
173impl TerminalLock<'_> {
174 pub fn enable_raw_mode(&mut self) -> io::Result<RawModeGuard<'_>> {
185 self.inner.enable_raw_mode().map(RawModeGuard)
186 }
187}
188
189impl sealed::Sealed for TerminalLock<'_> {}
190impl Transceive for TerminalLock<'_> {}
191
192impl io::Read for TerminalLock<'_> {
193 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
194 self.inner.read(buf)
195 }
196}
197
198impl io::Write for TerminalLock<'_> {
199 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
200 self.inner.write(buf)
201 }
202
203 fn flush(&mut self) -> io::Result<()> {
204 self.inner.flush()
205 }
206}
207
208#[derive(Debug)]
209struct StdioLocks {
210 #[allow(dead_code)]
211 stdin_lock: Option<io::StdinLock<'static>>,
212 #[allow(dead_code)]
213 stdout_lock: Option<io::StdoutLock<'static>>,
214 #[allow(dead_code)]
215 stderr_lock: Option<io::StderrLock<'static>>,
216}
217
218#[derive(Debug)]
221pub struct RawModeGuard<'a>(imp::RawModeGuard<'a>);
222
223impl sealed::Sealed for RawModeGuard<'_> {}
224impl Transceive for RawModeGuard<'_> {}
225
226impl io::Read for RawModeGuard<'_> {
227 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
228 self.0.read(buf)
229 }
230}
231
232impl io::Write for RawModeGuard<'_> {
233 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
234 self.0.write(buf)
235 }
236
237 fn flush(&mut self) -> io::Result<()> {
238 self.0.flush()
239 }
240}