unsegen_signals/lib.rs
1//! Use `unsegen`'s input module to raise signals on the usual key combinations (e.g., SIGINT on CTRL-C)
2//!
3//! # Example:
4//! ```should_panic
5//! extern crate unsegen;
6//! extern crate unsegen_signals;
7//!
8//! use unsegen::input::*;
9//! use unsegen_signals::*;
10//!
11//! for input in Input::read_all(&[b'a', b'b', b'c', 0o32/*Ctrl-Z*/, 0x3 /*Ctrl-C*/][..]) {
12//! let input = input.unwrap();
13//!
14//! input
15//! // Will send SIGINT and panic the test!
16//! .chain(SignalBehavior::new().on_default::<SIGINT>())
17//! // But Ctrl-Z will pass through here, because have no handler for SIGTSTP
18//! .chain(|i: Input| {
19//! match i.event {
20//! Event::Key(Key::Char(c)) => println!("Char: {}", c),
21//! Event::Key(Key::Ctrl(c)) => println!("Ctrl: {}", c),
22//! _ => return Some(i),
23//! }
24//! None
25//! });
26//! }
27//! ```
28extern crate libc;
29extern crate unsegen;
30
31use unsegen::input::{Behavior, Event, Input, Key, ToEvent};
32
33use libc::{getpid, kill};
34
35use std::collections::HashMap;
36
37type CSig = libc::c_int;
38
39/// Every signal is described by a type that ties together its `libc` representation and a default
40/// `Event` (i.e., most likely, a key combination) that fires it.
41pub trait Signal {
42 fn to_sig() -> CSig;
43 fn default_event() -> Event;
44}
45
46/// Type corresponding to SIGINT, by default triggered by [Ctrl-Z](https://en.wikipedia.org/wiki/Ctrl-C).
47pub struct SIGINT;
48/// Type corresponding to SIGTSTP, by default triggered by [Ctrl-Z](https://en.wikipedia.org/wiki/Ctrl-Z).
49pub struct SIGTSTP;
50/// Type corresponding to SIGQUIT, by default triggered by [Ctrl-\](https://en.wikipedia.org/wiki/Ctrl-%5C).
51pub struct SIGQUIT;
52
53impl Signal for SIGINT {
54 fn to_sig() -> CSig {
55 libc::SIGINT
56 }
57 fn default_event() -> Event {
58 Event::Key(Key::Ctrl('c'))
59 }
60}
61
62impl Signal for SIGTSTP {
63 fn to_sig() -> CSig {
64 libc::SIGTSTP
65 }
66 fn default_event() -> Event {
67 Event::Key(Key::Ctrl('z'))
68 }
69}
70
71impl Signal for SIGQUIT {
72 fn to_sig() -> CSig {
73 libc::SIGQUIT
74 }
75 fn default_event() -> Event {
76 Event::Key(Key::Ctrl('\\'))
77 }
78}
79
80/// Raises signals which will be passed to the underlying terminal.
81pub struct SignalBehavior {
82 mapping: HashMap<Event, CSig>,
83}
84
85impl SignalBehavior {
86 /// Create the Behavior without any triggers.
87 ///
88 /// Add triggers using `on` or `on_default`.
89 pub fn new() -> Self {
90 SignalBehavior {
91 mapping: HashMap::new(),
92 }
93 }
94
95 /// Raise a signal on a specific event.
96 pub fn on<S: Signal, E: ToEvent>(mut self, e: E) -> Self {
97 self.mapping.insert(e.to_event(), S::to_sig());
98 self
99 }
100
101 /// Raise a signal on the default event
102 pub fn on_default<S: Signal>(self) -> Self {
103 self.on::<S, Event>(S::default_event())
104 }
105}
106
107impl Behavior for SignalBehavior {
108 fn input(self, i: Input) -> Option<Input> {
109 if let Some(sig) = self.mapping.get(&i.event) {
110 unsafe {
111 kill(getpid(), *sig);
112 }
113 None
114 } else {
115 Some(i)
116 }
117 }
118}