use super::{ConsoleControl, HandlerContract};
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug, Clone, Copy, Default)]
pub struct UnixConsoleControl;
impl UnixConsoleControl {
pub const fn new() -> Self {
Self
}
}
impl crate::sealed::Sealed for UnixConsoleControl {}
#[derive(Clone, Copy)]
pub struct UnixModes(libc::termios);
unsafe impl Send for UnixModes {}
unsafe impl Sync for UnixModes {}
static ON_INTERRUPT: AtomicUsize = AtomicUsize::new(0);
const TRAPPED: [libc::c_int; 3] = [libc::SIGINT, libc::SIGTERM, libc::SIGHUP];
extern "C" fn on_signal(signal: libc::c_int) {
let f = ON_INTERRUPT.load(Ordering::SeqCst);
if f != 0 {
let f: fn() = unsafe { std::mem::transmute::<usize, fn()>(f) };
f();
}
unsafe { libc::_exit(128 + signal) };
}
impl ConsoleControl for UnixConsoleControl {
type Saved = UnixModes;
const HANDLER_CONTRACT: HandlerContract = HandlerContract::AsyncSignalSafe;
fn capture(&self) -> io::Result<Self::Saved> {
let mut modes: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(libc::STDIN_FILENO, &mut modes) } != 0 {
return Err(io::Error::last_os_error());
}
Ok(UnixModes(modes))
}
fn restore(&self, saved: &Self::Saved) -> io::Result<()> {
if unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &saved.0) } != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn write_raw(&self, bytes: &[u8]) {
unsafe {
libc::write(libc::STDOUT_FILENO, bytes.as_ptr().cast(), bytes.len());
}
}
fn trap(&self, on_interrupt: fn()) -> io::Result<()> {
ON_INTERRUPT.store(on_interrupt as usize, Ordering::SeqCst);
for signal in TRAPPED {
unsafe {
let mut action: libc::sigaction = std::mem::zeroed();
action.sa_sigaction = on_signal as *const () as libc::sighandler_t;
libc::sigemptyset(&mut action.sa_mask);
action.sa_flags = libc::SA_RESTART;
if libc::sigaction(signal, &action, std::ptr::null_mut()) != 0 {
return Err(io::Error::last_os_error());
}
}
}
Ok(())
}
fn interrupt_exit_code(&self) -> i32 {
128 + libc::SIGINT
}
}