Switched to high level signal_hook API.

This commit is contained in:
shy 2021-04-12 19:52:10 +02:00
parent fd325a88ca
commit 55e9003f8c
3 changed files with 63 additions and 99 deletions

View file

@ -1,5 +1,3 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::clock::Clock; use crate::clock::Clock;
pub struct Position { pub struct Position {
@ -9,7 +7,7 @@ pub struct Position {
pub struct Layout { pub struct Layout {
pub force_redraw: bool, // Redraw elements on screen. pub force_redraw: bool, // Redraw elements on screen.
pub force_recalc: Arc<AtomicBool>, // Recalculate position of elements. pub force_recalc: bool, // Recalculate position of elements.
pub width: u16, pub width: u16,
pub height: u16, pub height: u16,
clock_width: u16, clock_width: u16,
@ -28,11 +26,10 @@ pub struct Layout {
} }
impl Layout { impl Layout {
pub fn new(sigwinch: Arc<AtomicBool>) -> Layout { pub fn new() -> Layout {
Layout { Layout {
force_redraw: true, force_redraw: true,
// May be set by signal handler (SIGWINCH). force_recalc: true,
force_recalc: sigwinch,
width: 0, width: 0,
height: 0, height: 0,
clock_width: 0, clock_width: 0,
@ -51,10 +48,12 @@ impl Layout {
} }
} }
pub fn update(&mut self, clock: &Clock, force: bool) { pub fn update(&mut self, clock: &Clock, force: bool)
if self.force_recalc.swap(false, Ordering::Relaxed) || force { -> Result<(), std::io::Error>
let (width, height) = termion::terminal_size() {
.expect("Could not read terminal size!"); if self.force_recalc || force {
self.force_recalc = false;
let (width, height) = termion::terminal_size()?;
self.width = width; self.width = width;
self.height = height; self.height = height;
self.clock_width = clock.get_width(); self.clock_width = clock.get_width();
@ -63,6 +62,7 @@ impl Layout {
self.compute(clock.elapsed >= 3600); self.compute(clock.elapsed >= 3600);
self.force_redraw = true; self.force_redraw = true;
} }
Ok(())
} }
#[cfg(test)] #[cfg(test)]
@ -146,7 +146,7 @@ impl Layout {
pub fn set_roster_width(&mut self, width: u16) { pub fn set_roster_width(&mut self, width: u16) {
if self.width != width { if self.width != width {
self.roster_width = width; self.roster_width = width;
self.force_recalc.store(true, Ordering::Relaxed); self.force_recalc = true;
} }
} }
} }

View file

@ -1,4 +1,5 @@
extern crate termion; extern crate termion;
extern crate signal_hook;
pub mod alarm; pub mod alarm;
mod buffer; mod buffer;
pub mod clock; pub mod clock;
@ -10,9 +11,9 @@ mod tests;
use std::{env, process, thread, time}; use std::{env, process, thread, time};
use std::io::Write; use std::io::Write;
use std::sync::Arc; use signal_hook::consts::signal::*;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use signal_hook::iterator::Signals;
use signal_hook::{flag, low_level}; use signal_hook::low_level;
use termion::{clear, cursor, style}; use termion::{clear, cursor, style};
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
use termion::event::Key; use termion::event::Key;
@ -24,86 +25,70 @@ use alarm::{Countdown, exec_command};
pub use alarm::AlarmRoster; pub use alarm::AlarmRoster;
pub use consts::ui::*; pub use consts::ui::*;
const SIGTSTP: usize = signal_hook::consts::SIGTSTP as usize;
const SIGWINCH: usize = signal_hook::consts::SIGWINCH as usize;
const SIGCONT: usize = signal_hook::consts::SIGCONT as usize;
const SIGTERM: usize = signal_hook::consts::SIGTERM as usize;
const SIGINT: usize = signal_hook::consts::SIGINT as usize;
const SIGUSR1: usize = signal_hook::consts::SIGUSR1 as usize;
const SIGUSR2: usize = signal_hook::consts::SIGUSR2 as usize;
pub fn run( pub fn run(
config: Config, config: Config,
mut alarm_roster: AlarmRoster, mut alarm_roster: AlarmRoster,
signal: Arc<AtomicUsize>,
sigwinch: Arc<AtomicBool>,
spawned: &mut Option<process::Child>, spawned: &mut Option<process::Child>,
) -> Result<(), std::io::Error> ) -> Result<(), std::io::Error>
{ {
let mut layout = Layout::new(sigwinch); let mut layout = Layout::new();
// Initialise roster_width. // Initialise roster_width.
layout.set_roster_width(alarm_roster.width()); layout.set_roster_width(alarm_roster.width());
let mut clock = Clock::new(&config); let mut clock = Clock::new(&config);
let mut countdown = Countdown::new(); let mut countdown = Countdown::new();
let mut buffer = Buffer::new(); let mut buffer = Buffer::new();
// Are we in insert mode?
let mut insert_mode = false;
let async_stdin = termion::async_stdin(); let async_stdin = termion::async_stdin();
let mut input_keys = async_stdin.keys(); let mut input_keys = async_stdin.keys();
let stdout = std::io::stdout(); let stdout = std::io::stdout();
let mut stdout = stdout.lock().into_raw_mode()?; let mut stdout = stdout.lock().into_raw_mode()?;
// Are we in insert mode?
let mut insert_mode = false;
// Register signals.
let mut signals = Signals::new(&[
SIGTSTP,
SIGCONT,
SIGWINCH,
SIGTERM,
SIGINT,
SIGUSR1,
SIGUSR2,
]).unwrap();
// Clear window and hide cursor. // Clear window and hide cursor.
write!(stdout, "{}{}", clear::All, cursor::Hide)?; write!(stdout, "{}{}", clear::All, cursor::Hide)?;
// Main loop entry. // Main loop entry.
loop { loop {
// Process received signals. // Process received signals.
match signal.swap(0, Ordering::Relaxed) { 'outer: for signal in signals.pending() {
// No signal received. match signal {
0 => (), // Suspend execution on SIGTSTP.
// Suspend execution on SIGTSTP. SIGTSTP => suspend(&mut stdout)?,
SIGTSTP => { // Continuing after SIGTSTP or SIGSTOP.
suspend(&mut stdout)?; SIGCONT => {
// Clear SIGCONT, as we have already taken care to reset the restore_after_suspend(&mut stdout)?;
// terminal. layout.force_redraw = true;
signal.compare_and_swap(SIGCONT, 0, Ordering::Relaxed); },
layout.force_redraw = true; SIGWINCH => layout.force_recalc = true,
// Jump to the start of the main loop. // Exit main loop on SIGTERM and SIGINT.
continue; SIGTERM | SIGINT => break 'outer,
}, // Reset clock on SIGUSR1.
// Continuing after SIGSTOP. SIGUSR1 => {
SIGCONT => { clock.reset();
// This is reached when the process was suspended by SIGSTOP. alarm_roster.reset_all();
restore_after_suspend(&mut stdout)?; layout.force_recalc = true;
layout.force_redraw = true; layout.force_redraw = true;
}, },
// Exit main loop on SIGTERM and SIGINT. // (Un-)Pause clock on SIGUSR2.
SIGTERM | SIGINT => break, SIGUSR2 => clock.toggle(),
// Reset clock on SIGUSR1. // We didn't register anything else.
SIGUSR1 => { _ => unreachable!(),
clock.reset(); }
alarm_roster.reset_all();
layout.force_recalc.store(true, Ordering::Relaxed);
layout.force_redraw = true;
},
// (Un-)Pause clock on SIGUSR2.
SIGUSR2 => clock.toggle(),
// We didn't register anything else.
_ => unreachable!(),
} }
// Update input buffer display, if requested.
/*
if buffer.altered {
buffer.draw(&mut stdout, &mut layout)?;
stdout.flush()?;
}
*/
// Update elapsed time. // Update elapsed time.
let elapsed = if clock.paused { let elapsed = if clock.paused {
clock.elapsed clock.elapsed
@ -123,13 +108,13 @@ pub fn run(
clock.next_day(); clock.next_day();
// "clock.elapsed" set by "clock.next_day()". // "clock.elapsed" set by "clock.next_day()".
alarm_roster.reset_all(); alarm_roster.reset_all();
layout.force_recalc.store(true, Ordering::Relaxed); layout.force_recalc = true;
} }
// Update window size information and calculate the clock position. // Update window size information and calculate the clock position.
// Also enforce recalculation of layout if we start displaying // Also enforce recalculation of layout if we start displaying
// hours. // hours.
layout.update(&clock, clock.elapsed == 3600); layout.update(&clock, clock.elapsed == 3600)?;
// Check for exceeded alarms. // Check for exceeded alarms.
if let Some((time, label)) = alarm_roster.check(&mut clock, &layout, &mut countdown) { if let Some((time, label)) = alarm_roster.check(&mut clock, &layout, &mut countdown) {
@ -269,7 +254,7 @@ pub fn run(
Key::Char('r') => { Key::Char('r') => {
clock.reset(); clock.reset();
alarm_roster.reset_all(); alarm_roster.reset_all();
layout.force_recalc.store(true, Ordering::Relaxed); layout.force_recalc = true;
layout.force_redraw = true; layout.force_redraw = true;
}, },
// (Un-)Pause on space. // (Un-)Pause on space.
@ -301,7 +286,7 @@ pub fn run(
suspend(&mut stdout)?; suspend(&mut stdout)?;
// Clear SIGCONT, as we have already taken care to reset // Clear SIGCONT, as we have already taken care to reset
// the terminal. // the terminal.
signal.compare_and_swap(SIGCONT, 0, Ordering::Relaxed); //signal.compare_and_swap(SIGCONT, 0, Ordering::Relaxed);
layout.force_redraw = true; layout.force_redraw = true;
// Jump to the start of the main loop. // Jump to the start of the main loop.
continue; continue;
@ -430,22 +415,8 @@ impl Config {
} }
} }
pub fn register_signals(
signal: &Arc<AtomicUsize>,
recalc_flag: &Arc<AtomicBool>,
) {
flag::register_usize(SIGTSTP as i32, Arc::clone(&signal), SIGTSTP).unwrap();
flag::register_usize(SIGCONT as i32, Arc::clone(&signal), SIGCONT).unwrap();
flag::register_usize(SIGTERM as i32, Arc::clone(&signal), SIGTERM).unwrap();
flag::register_usize(SIGINT as i32, Arc::clone(&signal), SIGINT).unwrap();
flag::register_usize(SIGUSR1 as i32, Arc::clone(&signal), SIGUSR1).unwrap();
flag::register_usize(SIGUSR2 as i32, Arc::clone(&signal), SIGUSR2).unwrap();
// SIGWINCH sets "force_recalc" directly.
flag::register(SIGWINCH as i32, Arc::clone(&recalc_flag)).unwrap();
}
// Prepare to suspend execution. Called on SIGTSTP. // Prepare to suspend execution. Called on SIGTSTP.
fn suspend<W: Write>(mut stdout: &mut RawTerminal<W>) fn suspend<W: Write>(stdout: &mut RawTerminal<W>)
-> Result<(), std::io::Error> -> Result<(), std::io::Error>
{ {
write!(stdout, write!(stdout,
@ -463,7 +434,8 @@ fn suspend<W: Write>(mut stdout: &mut RawTerminal<W>)
eprintln!("Error raising SIGTSTP: {}", error); eprintln!("Error raising SIGTSTP: {}", error);
} }
restore_after_suspend(&mut stdout) //restore_after_suspend(&mut stdout)
Ok(())
} }
// Set up terminal after SIGTSTP or SIGSTOP. // Set up terminal after SIGTSTP or SIGSTOP.

View file

@ -1,10 +1,6 @@
extern crate signal_hook;
use std::{env, process}; use std::{env, process};
use std::io::Write; use std::io::Write;
use std::sync::Arc; use kitchentimer::{Config, AlarmRoster, run};
use std::sync::atomic::{AtomicBool, AtomicUsize};
use kitchentimer::{Config, AlarmRoster, register_signals, run};
fn main() { fn main() {
@ -17,15 +13,11 @@ fn main() {
process::exit(1); process::exit(1);
}); });
// Register signal handlers.
let signal = Arc::new(AtomicUsize::new(0));
let sigwinch = Arc::new(AtomicBool::new(true));
register_signals(&signal, &sigwinch);
// Holds spawned child process if any. // Holds spawned child process if any.
let mut spawned: Option<process::Child> = None; let mut spawned: Option<process::Child> = None;
// Run main loop. // Run main loop.
if let Err(e) = run(config, alarm_roster, signal, sigwinch, &mut spawned) { if let Err(e) = run(config, alarm_roster, &mut spawned) {
println!("Main loop exited with error: {}", e); println!("Main loop exited with error: {}", e);
process::exit(1); process::exit(1);
} }