summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-02-03 17:25:14 +0200
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-01 18:57:51 +0300
commitf52e7b170a50b7a5462ae35ba04cf556268d6854 (patch)
tree218ffc86bb61a5751b9d1bad86258e8ac11e508f
parent0855fa9bd95594d18f89f931341eba4a8acbd312 (diff)
Send SIGINT on Ctrl-C
- Send Ctrl-C once for graceful exit. An atomic bool flag is set and then main process gets a signal and then quits just like if 'q' was issued. - Sending Ctrl-C again before the main process reads the signal (probably because it's frozen) forces a terminal restore and quits with 130.
-rw-r--r--src/main.rs43
-rw-r--r--src/state.rs77
-rw-r--r--src/terminal/keys.rs14
3 files changed, 100 insertions, 34 deletions
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<crossbeam::channel::Receiver<c_int>, Error> {
+fn notify(
+ signals: &[c_int],
+ exit_flag: Arc<AtomicBool>,
+ state: Arc<AtomicPtr<StateStdout>>,
+) -> Result<crossbeam::channel::Receiver<c_int>, 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<crossbeam::channel::Receiver<c_int>, 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<AtomicBool> = 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<termion::raw::RawTerminal<std::io::Stdout>>;
struct InputHandler {
@@ -79,7 +83,7 @@ pub struct UIState {
rows: usize,
grid: CellBuffer,
- stdout: Option<StateStdout>,
+ pub stdout: Option<Arc<AtomicPtr<StateStdout>>>,
components: Vec<Box<dyn Component>>,
pub dirty_areas: VecDeque<Area>,
sender: Sender<ThreadEvent>,
@@ -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<StateStdout> =
+ 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<AtomicPtr<StateStdout>>) {
+ let mut stdout: Box<StateStdout> =
+ 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")
+);