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}