summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorremgodow <remgodow@users.noreply.github.com>2020-09-14 15:57:41 +0200
committerGitHub <noreply@github.com>2020-09-14 15:57:41 +0200
commite8a3f31348d48bafaa2dbe818cf7bc850d4d3ec5 (patch)
tree9c1f08c179119b898a940eabcb679c8cb985477b
parentd993d070a319c164096e6a1dc7b81cf3d3f409a2 (diff)
feat(ui): crossplatform terminal resize handling using crossterm Event::Resize (#186)
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs63
-rw-r--r--src/os/shared.rs45
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap6
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap6
-rw-r--r--src/tests/cases/test_utils.rs25
-rw-r--r--src/tests/cases/ui.rs34
-rw-r--r--src/tests/fakes/fake_input.rs18
9 files changed, 57 insertions, 142 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2c0e8e2..5eef26e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -100,7 +100,6 @@ dependencies = [
"procfs 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"resolv-conf 0.6.3 (git+https://github.com/tailhook/resolv-conf?rev=83c0f25ebcb0615550488692c5213ca1ae4acd8f)",
- "signal-hook 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"sysinfo 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index b2411ef..f5fcbc7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,7 +24,6 @@ ipnetwork = "0.16.0"
tui = { version = "0.5", default-features = false, features = ["crossterm"]}
crossterm = "0.17.7"
structopt = "0.3"
-signal-hook = "0.1.10"
failure = "0.1.6"
chrono = "0.4"
regex = "1.3.1"
diff --git a/src/main.rs b/src/main.rs
index 82fd65b..c9ff945 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,7 +11,6 @@ use network::{
dns::{self, IpTable},
LocalSocket, Sniffer, Utilization,
};
-use os::OnSigWinch;
use ::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use ::crossterm::terminal;
@@ -104,10 +103,8 @@ pub struct OsInputOutput {
pub network_interfaces: Vec<NetworkInterface>,
pub network_frames: Vec<Box<dyn DataLinkReceiver>>,
pub get_open_sockets: fn() -> OpenSockets,
- pub keyboard_events: Box<dyn Iterator<Item = Event> + Send>,
+ pub terminal_events: Box<dyn Iterator<Item = Event> + Send>,
pub dns_client: Option<dns::Client>,
- pub on_winch: Box<OnSigWinch>,
- pub cleanup: Box<dyn Fn() + Send>,
pub write_to_stdout: Box<dyn FnMut(String) + Send>,
}
@@ -124,52 +121,16 @@ where
let mut active_threads = vec![];
- let keyboard_events = os_input.keyboard_events;
+ let terminal_events = os_input.terminal_events;
let get_open_sockets = os_input.get_open_sockets;
let mut write_to_stdout = os_input.write_to_stdout;
let mut dns_client = os_input.dns_client;
- let on_winch = os_input.on_winch;
- let cleanup = os_input.cleanup;
let raw_mode = opts.raw;
let network_utilization = Arc::new(Mutex::new(Utilization::new()));
let ui = Arc::new(Mutex::new(Ui::new(terminal_backend, opts.render_opts)));
- if !raw_mode {
- active_threads.push(
- thread::Builder::new()
- .name("resize_handler".to_string())
- .spawn({
- let ui = ui.clone();
- let paused = paused.clone();
- let cumulative_time = cumulative_time.clone();
- let last_start_time = last_start_time.clone();
- let ui_offset = ui_offset.clone();
-
- move || {
- on_winch({
- Box::new(move || {
- let mut ui = ui.lock().unwrap();
- let paused = paused.load(Ordering::SeqCst);
- ui.draw(
- paused,
- dns_shown,
- elapsed_time(
- *last_start_time.read().unwrap(),
- *cumulative_time.read().unwrap(),
- paused,
- ),
- ui_offset.load(Ordering::SeqCst),
- );
- })
- });
- }
- })
- .unwrap(),
- );
- }
-
let display_handler = thread::Builder::new()
.name("display_handler".to_string())
.spawn({
@@ -232,16 +193,31 @@ where
active_threads.push(
thread::Builder::new()
- .name("stdin_handler".to_string())
+ .name("terminal_events_handler".to_string())
.spawn({
let running = running.clone();
let display_handler = display_handler.thread().clone();
move || {
- for evt in keyboard_events {
+ for evt in terminal_events {
let mut ui = ui.lock().unwrap();
match evt {
+ Event::Resize(_x, _y) => {
+ if !raw_mode {
+ let paused = paused.load(Ordering::SeqCst);
+ ui.draw(
+ paused,
+ dns_shown,
+ elapsed_time(
+ *last_start_time.read().unwrap(),
+ *cumulative_time.read().unwrap(),
+ paused,
+ ),
+ ui_offset.load(Ordering::SeqCst),
+ );
+ };
+ }
Event::Key(KeyEvent {
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('c'),
@@ -251,7 +227,6 @@ where
code: KeyCode::Char('q'),
}) => {
running.store(false, Ordering::Release);
- cleanup();
display_handler.unpark();
match terminal::disable_raw_mode() {
Ok(_) => {}
diff --git a/src/os/shared.rs b/src/os/shared.rs
index ea0f44d..6731531 100644
--- a/src/os/shared.rs
+++ b/src/os/shared.rs
@@ -9,8 +9,6 @@ use ::tokio::runtime::Runtime;
use ::std::time;
use crate::os::errors::GetInterfaceErrorKind;
-#[cfg(not(target_os = "windows"))]
-use signal_hook::iterator::Signals;
#[cfg(target_os = "linux")]
use crate::os::linux::get_open_sockets;
@@ -21,12 +19,9 @@ use crate::os::windows::get_open_sockets;
use crate::{network::dns, OsInputOutput};
-pub type OnSigWinch = dyn Fn(Box<dyn Fn()>) + Send;
-pub type SigCleanup = dyn Fn() + Send;
+pub struct TerminalEvents;
-pub struct KeyboardEvents;
-
-impl Iterator for KeyboardEvents {
+impl Iterator for TerminalEvents {
type Item = Event;
fn next(&mut self) -> Option<Event> {
match read() {
@@ -67,35 +62,6 @@ fn get_interface(interface_name: &str) -> Option<NetworkInterface> {
.find(|iface| iface.name == interface_name)
}
-#[cfg(not(target_os = "windows"))]
-fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
- let signals = Signals::new(&[signal_hook::SIGWINCH]).unwrap();
- let on_winch = {
- let signals = signals.clone();
- move |cb: Box<dyn Fn()>| {
- for signal in signals.forever() {
- match signal {
- signal_hook::SIGWINCH => cb(),
- _ => unreachable!(),
- }
- }
- }
- };
- let cleanup = move || {
- signals.close();
- };
- (Box::new(on_winch), Box::new(cleanup))
-}
-
-#[cfg(any(target_os = "windows"))]
-fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
- let on_winch = { move |_cb: Box<dyn Fn()>| {} };
- let cleanup = move || {
- println!("Fake signal cleanup");
- };
- (Box::new(on_winch), Box::new(cleanup))
-}
-
fn create_write_to_stdout() -> Box<dyn FnMut(String) + Send> {
Box::new({
let mut stdout = io::stdout();
@@ -231,9 +197,8 @@ pub fn get_input(
failure::bail!("Failed to find any network interface to listen on.");
}
- let keyboard_events = Box::new(KeyboardEvents);
+ let keyboard_events = Box::new(TerminalEvents);
let write_to_stdout = create_write_to_stdout();
- let (on_winch, cleanup) = sigwinch();
let dns_client = if resolve {
let mut runtime = Runtime::new()?;
let resolver = match runtime.block_on(dns::Resolver::new(runtime.handle().clone())) {
@@ -253,10 +218,8 @@ pub fn get_input(
network_interfaces,
network_frames: available_network_frames,
get_open_sockets,
- keyboard_events,
+ terminal_events: keyboard_events,
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
})
}
diff --git a/src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap b/src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap
index 9365740..7707702 100644
--- a/src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap
+++ b/src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap
@@ -2,9 +2,11 @@
source: src/tests/cases/ui.rs
expression: "&terminal_draw_events_mirror[1]"
---
+ 21Bps / 0Bps
+ 1 1 21Bps / 0Bps 1.1.1.1 1 21Bps / 0Bps
@@ -28,9 +30,7 @@ expression: "&terminal_draw_events_mirror[1]"
-
-
-
+ <interface_name>:443 => 1.1.1.1:12345 (tcp) 1 21Bps / 0Bps
diff --git a/src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap b/src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap
index e973e48..d710afb 100644
--- a/src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap
+++ b/src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap
@@ -2,11 +2,9 @@
source: src/tests/cases/ui.rs
expression: "&terminal_draw_events_mirror[2]"
---
- 21Bps / 0Bps
- 1 1 21Bps / 0Bps 1.1.1.1 1 21Bps / 0Bps
@@ -30,7 +28,9 @@ expression: "&terminal_draw_events_mirror[2]"
- <interface_name>:443 => 1.1.1.1:12345 (tcp) 1 21Bps / 0Bps
+
+
+
diff --git a/src/tests/cases/test_utils.rs b/src/tests/cases/test_utils.rs
index 7982a2d..bd90399 100644
--- a/src/tests/cases/test_utils.rs
+++ b/src/tests/cases/test_utils.rs
@@ -1,6 +1,6 @@
use crate::tests::fakes::{
- create_fake_dns_client, create_fake_on_winch, get_interfaces, get_open_sockets, KeyboardEvents,
- NetworkFrames, TerminalEvent, TestBackend,
+ create_fake_dns_client, get_interfaces, get_open_sockets, NetworkFrames, TerminalEvent,
+ TerminalEvents, TestBackend,
};
use std::iter;
@@ -17,13 +17,23 @@ use packet_builder::payload::PayloadData;
use pnet::packet::Packet;
use pnet_base::MacAddr;
-pub fn sleep_and_quit_events(sleep_num: usize) -> Box<KeyboardEvents> {
+pub fn sleep_and_quit_events(sleep_num: usize) -> Box<TerminalEvents> {
let mut events: Vec<Option<Event>> = iter::repeat(None).take(sleep_num).collect();
events.push(Some(Event::Key(KeyEvent {
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char('c'),
})));
- Box::new(KeyboardEvents::new(events))
+ Box::new(TerminalEvents::new(events))
+}
+
+pub fn sleep_resize_and_quit_events(sleep_num: usize) -> Box<TerminalEvents> {
+ let mut events: Vec<Option<Event>> = iter::repeat(None).take(sleep_num).collect();
+ events.push(Some(Event::Resize(100, 100)));
+ events.push(Some(Event::Key(KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('c'),
+ })));
+ Box::new(TerminalEvents::new(events))
}
pub fn build_tcp_packet(
@@ -114,9 +124,6 @@ pub fn os_input_output_factory(
dns_client: Option<Client>,
keyboard_events: Box<dyn Iterator<Item = Event> + Send>,
) -> OsInputOutput {
- let on_winch = create_fake_on_winch(false);
- let cleanup = Box::new(|| {});
-
let write_to_stdout: Box<dyn FnMut(String) + Send> = match stdout {
Some(stdout) => Box::new({
move |output: String| {
@@ -131,10 +138,8 @@ pub fn os_input_output_factory(
network_interfaces: get_interfaces(),
network_frames,
get_open_sockets,
- keyboard_events,
+ terminal_events: keyboard_events,
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
}
}
diff --git a/src/tests/cases/ui.rs b/src/tests/cases/ui.rs
index 1037a67..9bbcf86 100644
--- a/src/tests/cases/ui.rs
+++ b/src/tests/cases/ui.rs
@@ -1,6 +1,6 @@
use crate::tests::fakes::TerminalEvent::*;
use crate::tests::fakes::{
- create_fake_dns_client, create_fake_on_winch, get_interfaces, get_open_sockets, NetworkFrames,
+ create_fake_dns_client, get_interfaces, get_open_sockets, NetworkFrames,
};
use ::insta::assert_snapshot;
@@ -10,13 +10,13 @@ use ::std::net::IpAddr;
use crate::tests::cases::test_utils::{
build_tcp_packet, opts_ui, os_input_output, os_input_output_factory, sample_frames,
- sleep_and_quit_events, test_backend_factory,
+ sleep_and_quit_events, sleep_resize_and_quit_events, test_backend_factory,
};
use ::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use pnet::datalink::DataLinkReceiver;
use std::iter;
-use crate::tests::fakes::KeyboardEvents;
+use crate::tests::fakes::TerminalEvents;
use crate::{start, Opt, OsInputOutput, RenderOpts};
@@ -81,7 +81,7 @@ fn pause_by_space() {
code: KeyCode::Char('c'),
})));
- let events = Box::new(KeyboardEvents::new(events));
+ let events = Box::new(TerminalEvents::new(events));
let os_input = os_input_output_factory(network_frames, None, None, events);
let (terminal_events, terminal_draw_events, backend) = test_backend_factory(190, 50);
let opts = opts_ui();
@@ -136,7 +136,7 @@ fn rearranged_by_tab() {
code: KeyCode::Char('c'),
})));
- let events = Box::new(KeyboardEvents::new(events));
+ let events = Box::new(TerminalEvents::new(events));
let os_input = os_input_output_factory(network_frames, None, None, events);
let (terminal_events, terminal_draw_events, backend) = test_backend_factory(190, 50);
let opts = opts_ui();
@@ -1077,18 +1077,14 @@ fn traffic_with_host_names() {
String::from("i-like-cheese.com"),
);
let dns_client = create_fake_dns_client(ips_to_hostnames);
- let on_winch = create_fake_on_winch(false);
- let cleanup = Box::new(|| {});
let write_to_stdout = Box::new(move |_output: String| {});
let os_input = OsInputOutput {
network_interfaces: get_interfaces(),
network_frames,
get_open_sockets,
- keyboard_events: sleep_and_quit_events(3),
+ terminal_events: sleep_and_quit_events(3),
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
};
let opts = opts_ui();
@@ -1186,18 +1182,14 @@ fn truncate_long_hostnames() {
String::from("i-like-cheese.com"),
);
let dns_client = create_fake_dns_client(ips_to_hostnames);
- let on_winch = create_fake_on_winch(false);
- let cleanup = Box::new(|| {});
let write_to_stdout = Box::new(move |_output: String| {});
let os_input = OsInputOutput {
network_interfaces: get_interfaces(),
network_frames,
get_open_sockets,
- keyboard_events: sleep_and_quit_events(3),
+ terminal_events: sleep_and_quit_events(3),
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
};
let opts = opts_ui();
@@ -1294,18 +1286,14 @@ fn no_resolve_mode() {
String::from("i-like-cheese.com"),
);
let dns_client = None;
- let on_winch = create_fake_on_winch(false);
- let cleanup = Box::new(|| {});
let write_to_stdout = Box::new(move |_output: String| {});
let os_input = OsInputOutput {
network_interfaces: get_interfaces(),
network_frames,
get_open_sockets,
- keyboard_events: sleep_and_quit_events(3),
+ terminal_events: sleep_and_quit_events(3),
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
};
let opts = opts_ui();
@@ -1338,18 +1326,14 @@ fn traffic_with_winch_event() {
let (terminal_events, terminal_draw_events, backend) = test_backend_factory(190, 50);
let dns_client = create_fake_dns_client(HashMap::new());
- let on_winch = create_fake_on_winch(true);
- let cleanup = Box::new(|| {});
let write_to_stdout = Box::new(move |_output: String| {});
let os_input = OsInputOutput {
network_interfaces: get_interfaces(),
network_frames,
get_open_sockets,
- keyboard_events: sleep_and_quit_events(2),
+ terminal_events: sleep_resize_and_quit_events(2),
dns_client,
- on_winch,
- cleanup,
write_to_stdout,
};
let opts = opts_ui();
diff --git a/src/tests/fakes/fake_input.rs b/src/tests/fakes/fake_input.rs
index 5e9e913..0fdf63a 100644
--- a/src/tests/fakes/fake_input.rs
+++ b/src/tests/fakes/fake_input.rs
@@ -13,21 +13,20 @@ use crate::{
dns::{self, Lookup},
Connection, Protocol,
},
- os::OnSigWinch,
OpenSockets,
};
-pub struct KeyboardEvents {
+pub struct TerminalEvents {
pub events: Vec<Option<Event>>,
}
-impl KeyboardEvents {
+impl TerminalEvents {
pub fn new(mut events: Vec<Option<Event>>) -> Self {
events.reverse(); // this is so that we do not have to shift the array
- KeyboardEvents { events }
+ TerminalEvents { events }
}
}
-impl Iterator for KeyboardEvents {
+impl Iterator for TerminalEvents {
type Item = Event;
fn next(&mut self) -> Option<Event> {
match self.events.pop() {
@@ -157,15 +156,6 @@ pub fn get_interfaces() -> Vec<NetworkInterface> {
}]
}
-pub fn create_fake_on_winch(should_send_winch_event: bool) -> Box<OnSigWinch> {
- Box::new(move |cb| {
- if should_send_winch_event {
- thread::sleep(time::Duration::from_millis(900));
- cb()
- }
- })
-}
-
pub fn create_fake_dns_client(ips_to_hosts: HashMap<IpAddr, String>) -> Option<dns::Client> {
let runtime = Runtime::new().unwrap();
let dns_client = dns::Client::new(FakeResolver(ips_to_hosts), runtime).unwrap();