Skip to content

Commit 4a171fc

Browse files
committed
➖ Embed terminal-trx
1 parent 7713b70 commit 4a171fc

File tree

17 files changed

+1203
-22
lines changed

17 files changed

+1203
-22
lines changed

Cargo.lock

Lines changed: 7 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/terminal-colorsaurus/Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@ cfg-if = "1.0.0"
1919
[target.'cfg(unix)'.dependencies]
2020
memchr = "2.7.1"
2121
mio = { version = "1", features = ["os-ext"], default-features = false }
22-
terminal-trx = "0.2.3"
23-
24-
[target.'cfg(target_os = "macos")'.dependencies]
2522
libc = "0.2.151"
2623

2724
[target.'cfg(windows)'.dependencies]
2825
memchr = "2.7.1"
29-
terminal-trx = "0.2.3"
30-
windows-sys = { version = "0.59.0", features = ["Win32_System_Threading"] } # Keep this in sync with terminal-trx's version to avoid duplicate deps.
26+
windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Console", "Win32_Storage_FileSystem", "Win32_Foundation"] }
27+
28+
[dev-dependencies]
29+
static_assertions = "1.1.0"
3130

3231
[lints.rust]
3332
missing_debug_implementations = "warn"

crates/terminal-colorsaurus/src/io/poll/macos.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use super::super::read_timed_out;
2+
use crate::trx::Transceive;
23
use libc::{c_int, pselect, time_t, timespec, FD_ISSET, FD_SET};
34
use std::io;
45
use std::mem::zeroed;
56
use std::ptr::{null, null_mut};
67
use std::time::Duration;
7-
use terminal_trx::Transceive;
88

99
// macOS does not support polling /dev/tty using kqueue, so we have to
1010
// resort to pselect/select. See https://nathancraddock.com/blog/macos-dev-tty-polling/.

crates/terminal-colorsaurus/src/io/poll/unix.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use super::super::read_timed_out;
2+
use crate::trx::Transceive;
23
use mio::unix::SourceFd;
34
use mio::{Events, Interest, Poll, Token};
45
use std::io;
56
use std::time::Duration;
6-
use terminal_trx::Transceive;
77

88
pub(crate) fn poll_read(terminal: &dyn Transceive, timeout: Duration) -> io::Result<()> {
99
if timeout.is_zero() {

crates/terminal-colorsaurus/src/io/poll/windows.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::super::read_timed_out;
2+
use crate::trx::Transceive;
23
use std::io;
34
use std::os::windows::io::AsRawHandle as _;
45
use std::time::Duration;
5-
use terminal_trx::Transceive;
66
use windows_sys::Win32::Foundation::{WAIT_ABANDONED, WAIT_OBJECT_0, WAIT_TIMEOUT};
77
use windows_sys::Win32::System::Threading::WaitForSingleObject;
88

crates/terminal-colorsaurus/src/io/term_reader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::poll_read;
2+
use crate::trx::Transceive;
23
use std::io;
34
use std::time::{Duration, Instant};
4-
use terminal_trx::Transceive;
55

66
#[derive(Debug)]
77
pub(crate) struct TermReader<R> {

crates/terminal-colorsaurus/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ cfg_if! {
5353
mod xparsecolor;
5454
mod xterm;
5555
use xterm as imp;
56+
mod trx;
5657
} else {
5758
mod unsupported;
5859
use unsupported as imp;
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#![cfg_attr(docsrs, feature(doc_cfg))]
2+
3+
//! Provides a handle to the terminal of the current process that is both readable and writable.
4+
5+
use cfg_if::cfg_if;
6+
use std::io;
7+
use std::marker::PhantomData;
8+
use std::sync::{Mutex, MutexGuard};
9+
10+
cfg_if! {
11+
if #[cfg(all(unix, not(terminal_colorsaurus_test_unsupported)))] {
12+
mod unix;
13+
use unix as imp;
14+
} else if #[cfg(all(windows, not(terminal_colorsaurus_test_unsupported)))] {
15+
mod windows;
16+
use windows as imp;
17+
} else {
18+
mod unsupported;
19+
use unsupported as imp;
20+
}
21+
}
22+
23+
static TERMINAL_LOCK: Mutex<()> = Mutex::new(());
24+
25+
/// Creates a readable and writable handle to the terminal (or TTY) if available.
26+
///
27+
/// Use [`Terminal::lock`] if you want to avoid locking before each read / write call.
28+
///
29+
/// ## Unix
30+
/// On Unix, the terminal is retrieved by successively testing
31+
/// * the standard error,
32+
/// * standard input,
33+
/// * standard output,
34+
/// * and finally `/dev/tty`.
35+
///
36+
/// ## Windows
37+
/// On Windows, the reading half is retrieved by first testing the standard input, falling back to `CONIN$`. \
38+
/// The writing half is retrieved by successfully testing
39+
/// * the standard error,
40+
/// * standard output,
41+
/// * and finally `CONOUT$`.
42+
pub fn terminal() -> io::Result<Terminal> {
43+
imp::terminal().map(Terminal)
44+
}
45+
46+
macro_rules! impl_transceive {
47+
($($extra_supertraits:tt)*) => {
48+
/// A trait for objects that are both [`io::Read`] and [`io::Write`].
49+
pub trait Transceive: io::Read + io::Write $($extra_supertraits)* + sealed::Sealed {}
50+
};
51+
}
52+
53+
cfg_if! {
54+
if #[cfg(terminal_colorsaurus_test_unsupported)] {
55+
impl_transceive! { }
56+
} else if #[cfg(unix)] {
57+
impl_transceive! { + std::os::fd::AsFd + std::os::fd::AsRawFd }
58+
} else if #[cfg(windows)] {
59+
impl_transceive! { + ConsoleHandles }
60+
} else {
61+
impl_transceive! { }
62+
}
63+
}
64+
65+
/// A trait to borrow the console handles from the underlying console.
66+
#[cfg(all(windows, not(terminal_colorsaurus_test_unsupported)))]
67+
#[cfg_attr(docsrs, doc(cfg(windows)))]
68+
pub trait ConsoleHandles {
69+
/// Returns a handle to the consoles's input buffer `CONIN$`.
70+
fn input_buffer_handle(&self) -> std::os::windows::io::BorrowedHandle<'_>;
71+
72+
/// Returns a handle to the consoles's screen buffer `CONOUT$`.
73+
#[allow(unused, reason = "Currently unused in terminal-colorsaurus")]
74+
fn screen_buffer_handle(&self) -> std::os::windows::io::BorrowedHandle<'_>;
75+
}
76+
77+
mod sealed {
78+
pub trait Sealed {}
79+
}
80+
81+
/// A readable and writable handle to the terminal (or TTY), created using [`terminal()`].
82+
/// You can read and write data using the [`io::Read`] and [`io::Write`] implementations respectively.
83+
///
84+
/// Use [`Terminal::lock`] if you want to avoid locking before each read / write call.
85+
#[derive(Debug)]
86+
pub struct Terminal(imp::Terminal);
87+
88+
#[cfg(test)]
89+
static_assertions::assert_impl_all!(Terminal: Send, Sync, std::panic::UnwindSafe, std::panic::RefUnwindSafe);
90+
91+
impl sealed::Sealed for Terminal {}
92+
impl Transceive for Terminal {}
93+
94+
impl io::Read for Terminal {
95+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
96+
self.lock().read(buf)
97+
}
98+
}
99+
100+
impl io::Write for Terminal {
101+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
102+
self.lock().write(buf)
103+
}
104+
105+
fn flush(&mut self) -> io::Result<()> {
106+
self.lock().flush()
107+
}
108+
}
109+
110+
impl Terminal {
111+
/// Locks access to this terminal, returing a guard that is readable and writable.
112+
///
113+
/// Until the returned [`TerminalLock`] is dropped, all standard I/O streams
114+
/// that refer to the same terminal will be locked.
115+
pub fn lock(&mut self) -> TerminalLock<'_> {
116+
let mutex_guard = TERMINAL_LOCK.lock().unwrap_or_else(|e| e.into_inner());
117+
let stdio_locks = self.0.lock_stdio();
118+
TerminalLock {
119+
inner: &mut self.0,
120+
_stdio_locks: stdio_locks,
121+
_mutex_guard: mutex_guard,
122+
_phantom_data: PhantomData,
123+
}
124+
}
125+
}
126+
127+
/// Guard for exclusive read- and write access to the terminal.
128+
/// Can be created using [`Terminal::lock`].
129+
#[derive(Debug)]
130+
pub struct TerminalLock<'a> {
131+
inner: &'a mut imp::Terminal,
132+
_mutex_guard: MutexGuard<'static, ()>,
133+
_stdio_locks: StdioLocks,
134+
_phantom_data: PhantomData<*mut ()>,
135+
}
136+
137+
#[cfg(test)]
138+
static_assertions::assert_not_impl_any!(TerminalLock<'_>: Send, Sync);
139+
140+
impl TerminalLock<'_> {
141+
/// Enables raw mode on this terminal for the lifetime of the returned guard.
142+
///
143+
/// Raw mode has two effects:
144+
/// * Input typed into the terminal is not visible.
145+
/// * Input is can be read immediately (usually input is only available after a newline character).
146+
/// * (Windows) Ensures that VT sequences are processed in both input and output.
147+
///
148+
/// ### Windows
149+
/// This function returns an [`Err`] with [`ErrorKind::Unsupported`](`io::ErrorKind::Unsupported`) if the standard input is
150+
/// connected to a MSYS/Cygwin terminal.
151+
pub fn enable_raw_mode(&mut self) -> io::Result<RawModeGuard<'_>> {
152+
self.inner.enable_raw_mode().map(RawModeGuard)
153+
}
154+
}
155+
156+
impl sealed::Sealed for TerminalLock<'_> {}
157+
impl Transceive for TerminalLock<'_> {}
158+
159+
impl<'a> io::Read for TerminalLock<'a> {
160+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
161+
self.inner.read(buf)
162+
}
163+
}
164+
165+
impl<'a> io::Write for TerminalLock<'a> {
166+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
167+
self.inner.write(buf)
168+
}
169+
170+
fn flush(&mut self) -> io::Result<()> {
171+
self.inner.flush()
172+
}
173+
}
174+
175+
#[derive(Debug)]
176+
struct StdioLocks {
177+
#[allow(dead_code)]
178+
stdin_lock: Option<io::StdinLock<'static>>,
179+
#[allow(dead_code)]
180+
stdout_lock: Option<io::StdoutLock<'static>>,
181+
#[allow(dead_code)]
182+
stderr_lock: Option<io::StderrLock<'static>>,
183+
}
184+
185+
/// Guard for raw mode on the terminal, disables raw mode on drop.
186+
/// Can be crated using [`TerminalLock::enable_raw_mode`].
187+
#[derive(Debug)]
188+
pub struct RawModeGuard<'a>(imp::RawModeGuard<'a>);
189+
190+
impl sealed::Sealed for RawModeGuard<'_> {}
191+
impl Transceive for RawModeGuard<'_> {}
192+
193+
impl<'a> io::Read for RawModeGuard<'a> {
194+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
195+
self.0.read(buf)
196+
}
197+
}
198+
199+
impl<'a> io::Write for RawModeGuard<'a> {
200+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
201+
self.0.write(buf)
202+
}
203+
204+
fn flush(&mut self) -> io::Result<()> {
205+
self.0.flush()
206+
}
207+
}

0 commit comments

Comments
 (0)