use anyhow::Result;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::signal;
use tracing::{debug, info};
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
pub fn is_interrupted() -> bool {
INTERRUPTED.load(Ordering::Relaxed)
}
pub fn reset_interrupt() {
INTERRUPTED.store(false, Ordering::Relaxed);
}
pub fn setup_signal_handlers() -> Result<Arc<AtomicBool>> {
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_clone = Arc::clone(&shutdown);
if let Err(e) = ctrlc::set_handler(move || {
info!("Received Ctrl+C signal");
INTERRUPTED.store(true, Ordering::Relaxed);
shutdown_clone.store(true, Ordering::Relaxed);
}) {
debug!("Could not set Ctrl-C handler: {}", e);
}
Ok(shutdown)
}
pub async fn setup_async_signal_handlers(shutdown: Arc<AtomicBool>) {
tokio::spawn(async move {
#[cfg(unix)]
{
let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("Failed to set up SIGTERM handler");
tokio::select! {
_ = sigterm.recv() => {
info!("Received SIGTERM signal");
shutdown.store(true, Ordering::Relaxed);
}
}
}
});
}
#[cfg(unix)]
pub async fn handle_terminal_resize() -> Result<tokio::sync::mpsc::Receiver<(u16, u16)>> {
const RESIZE_SIGNAL_CHANNEL_SIZE: usize = 16;
let (tx, rx) = tokio::sync::mpsc::channel(RESIZE_SIGNAL_CHANNEL_SIZE);
tokio::spawn(async move {
let mut sigwinch = signal::unix::signal(signal::unix::SignalKind::window_change())
.expect("Failed to set up SIGWINCH handler");
loop {
sigwinch.recv().await;
if let Ok((width, height)) = crossterm::terminal::size() {
debug!("Terminal resized to {}x{}", width, height);
let _ = tx.try_send((width, height));
}
}
});
Ok(rx)
}
pub struct TerminalGuard {
_original_hook: Option<Box<dyn Fn() + Send + Sync>>,
}
impl Default for TerminalGuard {
fn default() -> Self {
Self::new()
}
}
impl TerminalGuard {
pub fn new() -> Self {
let _original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = Self::restore_terminal();
eprintln!("\n{panic_info}");
}));
Self {
_original_hook: None, }
}
pub fn restore_terminal() -> Result<()> {
crate::pty::terminal::force_terminal_cleanup();
Ok(())
}
}
impl Drop for TerminalGuard {
fn drop(&mut self) {
let _ = Self::restore_terminal();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interrupt_flag() {
reset_interrupt();
assert!(!is_interrupted());
INTERRUPTED.store(true, Ordering::Relaxed);
assert!(is_interrupted());
reset_interrupt();
assert!(!is_interrupted());
}
#[test]
fn test_terminal_guard_creation() {
let _guard = TerminalGuard::new();
}
}