#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TerminalFileDescriptor(CharacterDeviceFileDescriptor);
impl AsRawFd for TerminalFileDescriptor
{
#[inline(always)]
fn as_raw_fd(&self) -> RawFd
{
self.0.as_raw_fd()
}
}
impl IntoRawFd for TerminalFileDescriptor
{
#[inline(always)]
fn into_raw_fd(self) -> RawFd
{
self.0.into_raw_fd()
}
}
impl FromRawFd for TerminalFileDescriptor
{
#[inline(always)]
unsafe fn from_raw_fd(fd: RawFd) -> Self
{
Self(CharacterDeviceFileDescriptor::from_raw_fd(fd))
}
}
impl FileDescriptor for TerminalFileDescriptor
{
}
impl OnDiskFileDescriptor for TerminalFileDescriptor
{
}
impl PipeLikeFileDescriptor for TerminalFileDescriptor
{
}
impl SpliceRecipient for TerminalFileDescriptor
{
}
impl SpliceSender for TerminalFileDescriptor
{
}
impl VectoredRead for TerminalFileDescriptor
{
vectored_read!();
}
impl VectoredWrite for TerminalFileDescriptor
{
vectored_write!();
}
impl Read for TerminalFileDescriptor
{
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
{
self.0.read(buf)
}
#[inline(always)]
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>
{
VectoredRead::read_vectored(self, unsafe { transmute(bufs) })
}
#[inline(always)]
unsafe fn initializer(&self) -> Initializer
{
self.0.initializer()
}
}
impl Write for TerminalFileDescriptor
{
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
{
self.0.write(buf)
}
#[inline(always)]
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize>
{
VectoredWrite::write_vectored(self, unsafe { transmute(bufs) })
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()>
{
let result = unsafe { tcdrain(self.as_raw_fd()) };
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
Err(io::Error::last_os_error())
}
else
{
unreachable_code(format_args!(""))
}
}
}
impl TerminalFileDescriptor
{
#[inline(always)]
pub fn open_terminal_character_device(terminal_character_device_file_path: impl AsRef<Path>, terminal_settings: &TerminalSettings) -> Result<Self, SpecialFileOpenError>
{
let this = Self(CharacterDeviceFileDescriptor::open_character_device_internal(terminal_character_device_file_path, O_NOCTTY)?);
this.change_terminal_settings(terminal_settings, WhenToChangeTerminalAttributes::Now, false).map_err(|terminal_settings_error| SpecialFileOpenError::Terminal(terminal_settings_error))?;
Ok(this)
}
#[inline(always)]
pub fn change_terminal_settings(&self, terminal_settings: &TerminalSettings, when: WhenToChangeTerminalAttributes, ignore_control_flags: bool) -> Result<(), TerminalSettingsError>
{
let mut terminal_options = self.get_terminal_settings()?;
terminal_settings.change_settings(&mut terminal_options);
Self::handle_terminal_settings_error(unsafe { tcsetattr(self.as_raw_fd(), when.flags(ignore_control_flags), &terminal_options) }, TerminalSettingsError::CouldNotSetTerminalAttributes)
}
#[inline(always)]
pub fn current_terminal_settings(&self) -> Result<CurrentTerminalSettings, TerminalSettingsError>
{
self.get_terminal_settings().map(|terminal_options| CurrentTerminalSettings(terminal_options))
}
#[inline(always)]
fn get_terminal_settings(&self) -> Result<termios, TerminalSettingsError>
{
let mut terminal_options: termios = unsafe_uninitialized();
Self::handle_terminal_settings_error(unsafe { tcgetattr(self.as_raw_fd(), &mut terminal_options) }, TerminalSettingsError::NotATerminal)?;
Ok(terminal_options)
}
#[inline(always)]
pub fn discard_input_received_but_not_read(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflush(self.as_raw_fd(), TCIFLUSH) })
}
#[inline(always)]
pub fn discard_output_written_but_not_transmitted(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflush(self.as_raw_fd(), TCOFLUSH) })
}
#[inline(always)]
pub fn discard_input_received_but_not_read_and_output_written_but_not_transmitted(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflush(self.as_raw_fd(), TCIOFLUSH) })
}
#[inline(always)]
pub fn send_break(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcsendbreak(self.as_raw_fd(), 0) })
}
#[inline(always)]
pub fn suspend_output(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflow(self.as_raw_fd(), TCOOFF) })
}
#[inline(always)]
pub fn resume_output(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflow(self.as_raw_fd(), TCOON) })
}
#[inline(always)]
pub fn stop(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflow(self.as_raw_fd(), TCIOFF) })
}
#[inline(always)]
pub fn start(&self) -> io::Result<()>
{
Self::handle_generic_io_error(unsafe { tcflow(self.as_raw_fd(), TCION) })
}
#[inline(always)]
pub fn session_identifier(&self) -> io::Result<ProcessGroupIdentifier>
{
let result = unsafe { tcgetsid(self.0.as_raw_fd()) };
if unlikely!(result == -1)
{
Err(io::Error::last_os_error())
}
else
{
ProcessGroupIdentifier::try_from(result).map_err(io_error_other)
}
}
#[inline(always)]
fn handle_terminal_settings_error(result: c_int, constructor: impl FnOnce(Errno) -> TerminalSettingsError) -> Result<(), TerminalSettingsError>
{
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
Err(constructor(errno()))
}
else
{
unreachable_code(format_args!(""))
}
}
#[inline(always)]
fn handle_generic_io_error(result: c_int) -> io::Result<()>
{
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
Err(io::Error::last_os_error())
}
else
{
unreachable_code(format_args!(""))
}
}
}