diff options
author | remgodow <remgodow@users.noreply.github.com> | 2020-09-14 15:57:41 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-14 15:57:41 +0200 |
commit | e8a3f31348d48bafaa2dbe818cf7bc850d4d3ec5 (patch) | |
tree | 9c1f08c179119b898a940eabcb679c8cb985477b | |
parent | d993d070a319c164096e6a1dc7b81cf3d3f409a2 (diff) |
feat(ui): crossplatform terminal resize handling using crossterm Event::Resize (#186)
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/main.rs | 63 | ||||
-rw-r--r-- | src/os/shared.rs | 45 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap | 6 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap | 6 | ||||
-rw-r--r-- | src/tests/cases/test_utils.rs | 25 | ||||
-rw-r--r-- | src/tests/cases/ui.rs | 34 | ||||
-rw-r--r-- | src/tests/fakes/fake_input.rs | 18 |
9 files changed, 57 insertions, 142 deletions
@@ -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)", @@ -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(); |