diff --git a/src/main.rs b/src/main.rs index 1afee8f..779c984 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,10 @@ use crossbeam::channel::{bounded, tick}; use crossbeam::select; use libc::c_int; use std::io::Error; +use std::sync::{ + atomic::{AtomicBool, AtomicPtr}, + Arc, +}; use std::time::Duration; //#[allow(dead_code)] @@ -131,8 +135,24 @@ pub mod username { } } -fn notify(signals: &[c_int]) -> Result, Error> { +fn notify( + signals: &[c_int], + exit_flag: Arc, + state: Arc>, +) -> Result, Error> { let (s, r) = bounded(100); + let _s = s.clone(); + let sigint_handler = move |_info: &nix::libc::siginfo_t| { + if exit_flag.load(std::sync::atomic::Ordering::SeqCst) { + crate::state::restore_to_main_screen(state.clone()); + std::process::exit(130); + } + exit_flag.store(true, std::sync::atomic::Ordering::SeqCst); + let _ = _s.send(signal_hook::SIGINT); + }; + unsafe { + signal_hook_registry::register_sigaction(signal_hook::SIGINT, sigint_handler)?; + } let signals = signal_hook::iterator::Signals::new(signals)?; std::thread::spawn(move || { for signal in signals.forever() { @@ -143,21 +163,28 @@ fn notify(signals: &[c_int]) -> Result, Erro } fn main() -> Result<(), Error> { + /* Create the application State */ + let mut state = UIState::new(); + let signals = &[ + /* signal_hook::SIGALRM, signal_hook::SIGTERM, signal_hook::SIGINT, signal_hook::SIGQUIT, + */ /* Catch SIGWINCH to handle terminal resizing */ signal_hook::SIGWINCH, ]; let ticker = tick(Duration::from_millis(1600)); - let signal_recvr = notify(signals)?; - - /* Create the application State */ - let mut state = UIState::new(); + let exit_flag: Arc = Arc::new(AtomicBool::new(false)); + let signal_recvr = notify( + signals, + exit_flag.clone(), + state.stdout.as_ref().unwrap().clone(), + )?; let receiver = state.receiver(); let window = Box::new(Window::new( @@ -177,13 +204,17 @@ fn main() -> Result<(), Error> { state.redraw(true); }, recv(signal_recvr) -> sig => { - eprintln!("got signal {:?}", sig); match sig.unwrap() { signal_hook::SIGWINCH => { state.update_size(); state.render(); state.redraw(true); }, + signal_hook::SIGINT => { + + drop(state); + break 'main; + } _ => {} } }, diff --git a/src/state.rs b/src/state.rs index dbce7b9..5216de0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -43,6 +43,10 @@ pub enum UIMode { Input, } +use std::sync::{ + atomic::{AtomicPtr, Ordering}, + Arc, +}; pub type StateStdout = termion::screen::AlternateScreen>; struct InputHandler { @@ -79,7 +83,7 @@ pub struct UIState { rows: usize, grid: CellBuffer, - stdout: Option, + pub stdout: Option>>, components: Vec>, pub dirty_areas: VecDeque, sender: Sender, @@ -118,15 +122,11 @@ impl UIState { let cols = termsize.map(|(w, _)| w).unwrap_or(0) as usize; let rows = termsize.map(|(_, h)| h).unwrap_or(0) as usize; - let _stdout = std::io::stdout(); - _stdout.lock(); - let stdout = AlternateScreen::from(_stdout.into_raw_mode().unwrap()); - let mut s = UIState { cols, rows, grid: CellBuffer::new(cols, rows, Cell::with_char(' ')), - stdout: Some(stdout), + stdout: None, components: Vec::with_capacity(1), sender, receiver, @@ -138,16 +138,7 @@ impl UIState { mode: UIMode::Normal, }; - write!( - s.stdout(), - "{}{}{}{}", - BracketModeStart, - cursor::Hide, - clear::All, - cursor::Goto(1, 1) - ) - .unwrap(); - s.flush(); + s.switch_to_alternate_screen(); s.restore_input(); s } @@ -157,31 +148,44 @@ impl UIState { pub fn switch_to_main_screen(&mut self) { write!( self.stdout(), - "{}{}{}", + "{}{}{}{}", termion::screen::ToMainScreen, cursor::Show, + RestoreWindowTitleIconFromStack, BracketModeEnd, ) .unwrap(); self.flush(); - self.stdout = None; + if let Some(stdout) = self.stdout.take() { + let termios: Box = + unsafe { std::boxed::Box::from_raw(stdout.load(Ordering::Relaxed)) }; + drop(termios); + } + self.input.kill(); } + pub fn switch_to_alternate_screen(&mut self) { let s = std::io::stdout(); - s.lock(); - self.stdout = Some(AlternateScreen::from(s.into_raw_mode().unwrap())); + + let mut stdout = AlternateScreen::from(s.into_raw_mode().unwrap()); write!( - self.stdout(), - "{}{}{}{}{}", + &mut stdout, + "{save_title_to_stack}{}{}{}{window_title}{}{}", termion::screen::ToAlternateScreen, cursor::Hide, clear::All, cursor::Goto(1, 1), BracketModeStart, + save_title_to_stack = SaveWindowTitleIconToStack, + window_title = "\x1b]2;bb\x07", ) .unwrap(); + + let termios = Box::new(stdout); + let stdout = std::sync::atomic::AtomicPtr::new(std::boxed::Box::into_raw(termios)); + self.stdout = Some(Arc::new(stdout)); self.flush(); } @@ -197,9 +201,12 @@ impl UIState { if termcols.unwrap_or(72) as usize != self.cols || termrows.unwrap_or(120) as usize != self.rows { - eprintln!( + std::dbg!( "Size updated, from ({}, {}) -> ({:?}, {:?})", - self.cols, self.rows, termcols, termrows + self.cols, + self.rows, + termcols, + termrows ); } self.cols = termcols.unwrap_or(72) as usize; @@ -340,16 +347,30 @@ impl UIState { } fn flush(&mut self) { - if let Some(s) = self.stdout.as_mut() { - s.flush().unwrap(); - } + self.stdout().flush().unwrap(); } fn stdout(&mut self) -> &mut StateStdout { - self.stdout.as_mut().unwrap() + unsafe { + self.stdout + .as_ref() + .unwrap() + .load(Ordering::Relaxed) + .as_mut() + .unwrap() + } } pub fn restore_input(&self) { self.input.restore(self.sender.clone()); } } + +pub fn restore_to_main_screen(stdout: Arc>) { + let mut stdout: Box = + unsafe { std::boxed::Box::from_raw(stdout.load(Ordering::SeqCst)) }; + let _ = stdout.flush(); + let _ = stdout.write_all(b"\x1B[?25h\x1B[?2004l"); + let _ = stdout.flush(); + drop(stdout); +} diff --git a/src/terminal/keys.rs b/src/terminal/keys.rs index 0393c51..43e8d7d 100644 --- a/src/terminal/keys.rs +++ b/src/terminal/keys.rs @@ -165,6 +165,10 @@ pub fn get_events( } }; match c { + Ok(TermionEvent::Key(TermionKey::Ctrl('c'))) if input_mode == InputMode::Normal => { + let self_pid = nix::unistd::Pid::this(); + nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGINT).unwrap(); + } Ok(TermionEvent::Key(k)) if input_mode == InputMode::Normal => { closure(Key::from(k)); } @@ -235,3 +239,13 @@ derive_csi_sequence!( pub const BRACKET_PASTE_START: &[u8] = b"\x1B[200~"; pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~"; + +derive_csi_sequence!( + #[doc = "`CSI Ps ; Ps ; Ps t`, where `Ps = 2 2 ; 0` -> Save xterm icon and window title on stack."] + (SaveWindowTitleIconToStack, "22;0t") +); + +derive_csi_sequence!( + #[doc = "Restore window title and icon from terminal's title stack. `CSI Ps ; Ps ; Ps t`, where `Ps = 2 3 ; 0` -> Restore xterm icon and window title from stack."] + (RestoreWindowTitleIconFromStack, "23;0t") +);