Switched to high level signal_hook API.
This commit is contained in:
parent
fd325a88ca
commit
55e9003f8c
3 changed files with 63 additions and 99 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
128
src/lib.rs
128
src/lib.rs
|
@ -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.
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue