From bce4a514769370308530fc44e53d5f2abf791119 Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 20:38:41 +0100 Subject: [PATCH 1/7] feat: stop the program in raw mode if we recive "stop" in stdin --- src/main.rs | 7 ++++++- src/os/shared.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8ace86fb..6473114b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,7 +54,12 @@ fn main() -> eyre::Result<()> { )?; } - let os_input = os::get_input(opts.interface.as_deref(), !opts.no_resolve, opts.dns_server)?; + let os_input = os::get_input( + opts.interface.as_deref(), + !opts.no_resolve, + opts.dns_server, + opts.raw, + )?; if opts.raw { let terminal_backend = RawTerminalBackend {}; start(terminal_backend, os_input, opts); diff --git a/src/os/shared.rs b/src/os/shared.rs index 511ffc1b..c88dd127 100644 --- a/src/os/shared.rs +++ b/src/os/shared.rs @@ -1,10 +1,10 @@ use std::{ - io::{self, ErrorKind, Write}, + io::{self, BufRead, ErrorKind, Write}, net::Ipv4Addr, time, }; -use crossterm::event::{read, Event}; +use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use eyre::{bail, eyre}; use itertools::Itertools; use log::{debug, warn}; @@ -44,6 +44,42 @@ impl Iterator for TerminalEvents { } } +pub struct StdinEvents { + stdin: io::Stdin, +} + +impl StdinEvents { + pub fn new() -> Self { + Self { stdin: io::stdin() } + } +} + +impl Iterator for StdinEvents { + type Item = Event; + fn next(&mut self) -> Option { + let mut line = String::new(); + match self.stdin.lock().read_line(&mut line) { + Ok(0) => None, // EOF + Ok(_) => { + let trimmed = line.trim(); + if trimmed == "stop" { + // Return a Ctrl+C event to trigger shutdown + Some(Event::Key(KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + state: crossterm::event::KeyEventState::empty(), + })) + } else { + // Continue reading, return a dummy event that won't trigger anything + self.next() + } + } + Err(_) => None, + } + } +} + pub(crate) fn get_datalink_channel( interface: &NetworkInterface, ) -> Result, GetInterfaceError> { @@ -96,6 +132,7 @@ pub fn get_input( interface_name: Option<&str>, resolve: bool, dns_server: Option, + raw_mode: bool, ) -> eyre::Result { // get the user's requested interface, if any // IDEA: allow requesting multiple interfaces @@ -207,10 +244,16 @@ pub fn get_input( let write_to_stdout = create_write_to_stdout(); + let terminal_events: Box + Send> = if raw_mode { + Box::new(StdinEvents::new()) + } else { + Box::new(TerminalEvents) + }; + Ok(OsInputOutput { interfaces_with_frames, get_open_sockets, - terminal_events: Box::new(TerminalEvents), + terminal_events, dns_client, write_to_stdout, }) From c13bedcd7326ef1b2437cb0e5b028f5216f3a550 Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 20:48:35 +0100 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e55fb949..c031b7c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed +* Stop the program in raw mode if we receive "stop" in stdin @sigmasd * Update CONTRIBUTING information #438 - @YJDoc2 @cyqsimon * Fix new clippy lint #457 - @cyqsimon * Apply new clippy lints #468 - @cyqsimon From 4974092c5ee068bfe712fa55dbd3a45f0af9e35c Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 20:53:51 +0100 Subject: [PATCH 3/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c031b7c7..3307c87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed -* Stop the program in raw mode if we receive "stop" in stdin @sigmasd +* Stop the program in raw mode if we receive "stop" in stdin - @sigmasd * Update CONTRIBUTING information #438 - @YJDoc2 @cyqsimon * Fix new clippy lint #457 - @cyqsimon * Apply new clippy lints #468 - @cyqsimon From 10a1c5eff4eb93dd588205a675844d47aef71ff5 Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 21:11:57 +0100 Subject: [PATCH 4/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3307c87a..28054f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed -* Stop the program in raw mode if we receive "stop" in stdin - @sigmasd +* Stop the program in raw mode if we receive "stop" in stdin #478 - @sigmasd * Update CONTRIBUTING information #438 - @YJDoc2 @cyqsimon * Fix new clippy lint #457 - @cyqsimon * Apply new clippy lints #468 - @cyqsimon From ca30b88a10538bc3cbd4267de603de5b0055eae2 Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 21:16:25 +0100 Subject: [PATCH 5/7] update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28054f56..ef83aa6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] -### Fixed +### Added * Stop the program in raw mode if we receive "stop" in stdin #478 - @sigmasd + +### Fixed + * Update CONTRIBUTING information #438 - @YJDoc2 @cyqsimon * Fix new clippy lint #457 - @cyqsimon * Apply new clippy lints #468 - @cyqsimon From 2067fa172ed79c52307f1bcfbee65f1458e5ebf3 Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Sun, 26 Oct 2025 21:21:22 +0100 Subject: [PATCH 6/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef83aa6f..4944cc17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added -* Stop the program in raw mode if we receive "stop" in stdin #478 - @sigmasd +* Stop the program in raw mode if we receive "stop" in stdin #478 - @sigmaSd ### Fixed From 9d59fd65f00e485d3fe9dc9f186287dcf3c3b31e Mon Sep 17 00:00:00 2001 From: Nbiba Bedis Date: Mon, 3 Nov 2025 09:09:39 +0100 Subject: [PATCH 7/7] use q instead of stop, and fix mutliple inputs handling --- CHANGELOG.md | 2 +- src/os/shared.rs | 73 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4944cc17..c2ed141c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added -* Stop the program in raw mode if we receive "stop" in stdin #478 - @sigmaSd +* Stop the program in raw mode if we receive "q" in stdin #478 - @sigmaSd ### Fixed diff --git a/src/os/shared.rs b/src/os/shared.rs index c88dd127..296b157a 100644 --- a/src/os/shared.rs +++ b/src/os/shared.rs @@ -1,7 +1,9 @@ use std::{ - io::{self, BufRead, ErrorKind, Write}, + io::{self, BufRead, BufReader, ErrorKind, Write}, net::Ipv4Addr, - time, + sync::mpsc::{channel, Receiver}, + thread, + time::{self, Duration}, }; use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; @@ -45,37 +47,64 @@ impl Iterator for TerminalEvents { } pub struct StdinEvents { - stdin: io::Stdin, + receiver: Receiver>, } impl StdinEvents { pub fn new() -> Self { - Self { stdin: io::stdin() } + let (sender, receiver) = channel(); + + // Spawn a thread to read from stdin + thread::spawn(move || { + let stdin = io::stdin(); + let reader = BufReader::new(stdin); + + for line in reader.lines() { + match line { + Ok(text) => { + let trimmed = text.trim(); + if trimmed == "q" { + // Send a 'q' key press event to trigger shutdown + let event = Some(Event::Key(KeyEvent { + code: KeyCode::Char('q'), + modifiers: KeyModifiers::NONE, + kind: KeyEventKind::Press, + state: crossterm::event::KeyEventState::empty(), + })); + if sender.send(event).is_err() { + break; + } + } + // For other input, just ignore and continue reading + } + Err(_) => { + // Error reading, send None to signal EOF + let _ = sender.send(None); + break; + } + } + } + // EOF reached, send None + let _ = sender.send(None); + }); + + Self { receiver } } } impl Iterator for StdinEvents { type Item = Event; fn next(&mut self) -> Option { - let mut line = String::new(); - match self.stdin.lock().read_line(&mut line) { - Ok(0) => None, // EOF - Ok(_) => { - let trimmed = line.trim(); - if trimmed == "stop" { - // Return a Ctrl+C event to trigger shutdown - Some(Event::Key(KeyEvent { - code: KeyCode::Char('c'), - modifiers: KeyModifiers::CONTROL, - kind: KeyEventKind::Press, - state: crossterm::event::KeyEventState::empty(), - })) - } else { - // Continue reading, return a dummy event that won't trigger anything - self.next() - } + // Try to receive with a timeout to avoid blocking forever + match self.receiver.recv_timeout(Duration::from_millis(100)) { + Ok(Some(event)) => Some(event), + Ok(None) => None, // EOF + Err(_) => { + // Timeout or channel error, just try again next time + // Return a dummy resize event to keep the loop going + // This won't trigger any action in raw mode + self.next() } - Err(_) => None, } } }