Implemented signal handling.

This commit is contained in:
shy 2021-04-05 17:27:24 +02:00
parent bde5bc9307
commit b27667d5e2
3 changed files with 76 additions and 11 deletions

25
Cargo.lock generated
View file

@ -10,15 +10,15 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
name = "kt" name = "kt"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"libc", "signal-hook",
"termion", "termion",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.91" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]] [[package]]
name = "numtoa" name = "numtoa"
@ -44,6 +44,25 @@ dependencies = [
"redox_syscall", "redox_syscall",
] ]
[[package]]
name = "signal-hook"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "termion" name = "termion"
version = "1.5.6" version = "1.5.6"

View file

@ -7,5 +7,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
termion = "1.5" termion = "1.5.6"
libc = "0.2" signal-hook = "0.3.8"

View file

@ -1,5 +1,5 @@
extern crate termion; extern crate termion;
extern crate libc; extern crate signal_hook;
mod alarm; mod alarm;
mod clock; mod clock;
mod common; mod common;
@ -7,6 +7,9 @@ mod layout;
use std::{time, thread, env}; use std::{time, thread, env};
use std::io::Write; use std::io::Write;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use signal_hook::flag;
use termion::{clear, color, cursor, style}; use termion::{clear, color, cursor, style};
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
use termion::event::Key; use termion::event::Key;
@ -24,11 +27,20 @@ const USAGE: &str =
-e, --exec [COMMAND] Execute \"COMMAND\" on alarm. Must be the last flag on -e, --exec [COMMAND] Execute \"COMMAND\" on alarm. Must be the last flag on
the command line. Everything after it is passed as the command line. Everything after it is passed as
argument to \"COMMAND\". Every \"%s\" will be replaced argument to \"COMMAND\". Every \"%s\" will be replaced
with the elapsed time in [(HH:)MM:SS] format."; with the elapsed time in [(HH:)MM:SS] format.
SIGNALS: <SIGUSR1> Reset clock.
<SIGUSR2> Pause or un-pause clock.";
const MENUBAR: &str = const MENUBAR: &str =
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit"; "[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
const MENUBAR_SHORT: &str = const MENUBAR_SHORT: &str =
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit"; "[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
// Needed for signal_hook.
const SIGTSTP: usize = signal_hook::consts::SIGTSTP 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 struct Config { pub struct Config {
plain: bool, plain: bool,
@ -55,6 +67,10 @@ fn main() {
let mut buffer = String::new(); let mut buffer = String::new();
let mut buffer_updated: bool = false; let mut buffer_updated: bool = false;
let mut countdown = Countdown::new(); let mut countdown = Countdown::new();
// Register signal handlers.
let signal = Arc::new(AtomicUsize::new(0));
register_signal_handlers(&signal);
// Clear screen and hide cursor. // Clear screen and hide cursor.
write!(stdout, write!(stdout,
@ -67,6 +83,29 @@ fn main() {
}); });
loop { loop {
// Process received signals.
match signal.swap(0, Ordering::Relaxed) {
// No signal received.
0 => (),
// Suspend execution on SIGTSTP.
SIGTSTP => {
suspend(&mut stdout);
layout.force_redraw = true;
},
// Exit main loop on SIGTERM and SIGINT.
SIGTERM | SIGINT => break,
// Reset clock on SIGUSR1.
SIGUSR1 => {
clock.reset();
alarm_roster.reset_all();
layout.force_redraw = true;
},
// (Un-)Pause clock on SIGUSR2.
SIGUSR2 => clock.toggle(),
// We didn't register anything else.
_ => unreachable!(),
}
// Process input. // Process input.
if let Some(key) = input_keys.next() { if let Some(key) = input_keys.next() {
match key.expect("Error reading input") { match key.expect("Error reading input") {
@ -269,7 +308,15 @@ fn parse_args(config: &mut Config) {
} }
} }
// Suspend execution by raising SIGTSTP. fn register_signal_handlers(signal: &Arc<AtomicUsize>) {
flag::register_usize(SIGTSTP as i32, Arc::clone(&signal), SIGTSTP).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();
}
// Suspend execution on SIGTSTP.
fn suspend<W: Write>(stdout: &mut RawTerminal<W>) { fn suspend<W: Write>(stdout: &mut RawTerminal<W>) {
write!(stdout, write!(stdout,
"{}{}{}", "{}{}{}",
@ -283,9 +330,8 @@ fn suspend<W: Write>(stdout: &mut RawTerminal<W>) {
eprintln!("Failed to leave raw terminal mode prior to suspend: {}", error); eprintln!("Failed to leave raw terminal mode prior to suspend: {}", error);
}); });
let result = unsafe { libc::raise(libc::SIGTSTP) }; if let Err(error) = signal_hook::low_level::emulate_default_handler(SIGTSTP as i32) {
if result != 0 { eprintln!("Error raising SIGTSTP: {}", error);
panic!("{}", std::io::Error::last_os_error());
} }
stdout.activate_raw_mode() stdout.activate_raw_mode()