use std::io;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use signal_hook::consts::signal::{SIGCHLD, SIGWINCH};
use signal_hook::iterator::Signals;
use tokio::sync::mpsc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PtySignalEvent {
WindowChanged,
ChildStateChanged,
}
#[derive(Debug)]
pub struct SignalHandle {
shutdown: Arc<AtomicBool>,
}
impl SignalHandle {
pub fn shutdown(&self) {
self.shutdown.store(true, Ordering::SeqCst);
}
}
impl Drop for SignalHandle {
fn drop(&mut self) {
self.shutdown();
}
}
pub fn start_signal_handler() -> io::Result<(mpsc::UnboundedReceiver<PtySignalEvent>, SignalHandle)>
{
let mut signals = Signals::new([SIGWINCH, SIGCHLD])?;
let (tx, rx) = mpsc::unbounded_channel();
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_clone = Arc::clone(&shutdown);
std::thread::Builder::new()
.name("pty-signal-handler".into())
.spawn(move || {
for signal in signals.forever() {
if shutdown_clone.load(Ordering::SeqCst) {
break;
}
let event = match signal {
SIGWINCH => PtySignalEvent::WindowChanged,
SIGCHLD => PtySignalEvent::ChildStateChanged,
_ => continue,
};
if tx.send(event).is_err() {
break;
}
}
})?;
Ok((rx, SignalHandle { shutdown }))
}
pub fn on_window_change<F>(callback: F) -> io::Result<SignalHandle>
where
F: Fn() + Send + 'static,
{
let mut signals = Signals::new([SIGWINCH])?;
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_clone = Arc::clone(&shutdown);
std::thread::Builder::new()
.name("pty-sigwinch-handler".into())
.spawn(move || {
for _ in signals.forever() {
if shutdown_clone.load(Ordering::SeqCst) {
break;
}
callback();
}
})?;
Ok(SignalHandle { shutdown })
}
#[must_use]
pub const fn is_sigchld(signal: i32) -> bool {
signal == SIGCHLD
}
#[must_use]
pub const fn is_sigwinch(signal: i32) -> bool {
signal == SIGWINCH
}
#[must_use]
pub const fn sigwinch() -> i32 {
SIGWINCH
}
#[must_use]
pub const fn sigchld() -> i32 {
SIGCHLD
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn signal_constants() {
assert!(is_sigchld(sigchld()));
assert!(is_sigwinch(sigwinch()));
assert!(!is_sigchld(sigwinch()));
assert!(!is_sigwinch(sigchld()));
}
#[test]
fn signal_handle_shutdown() {
let shutdown = Arc::new(AtomicBool::new(false));
let handle = SignalHandle {
shutdown: Arc::clone(&shutdown),
};
assert!(!shutdown.load(Ordering::SeqCst));
handle.shutdown();
assert!(shutdown.load(Ordering::SeqCst));
}
}