diff --git a/src/lib.rs b/src/lib.rs index f51ac7a..fe849f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,11 +136,34 @@ impl Multiview { pub fn push_stderr(&mut self, (i, j): (u16, u16), content: String) { self.push_stdout((i, j), content); } + + /// Restarts the selected tile. + pub fn restart(&mut self) -> io::Result<()> { + let tile = self.tile_mut(self.selected); + tile.restart() + } + + /// Restarts all tiles. + pub fn restart_all(&mut self) -> io::Result<()> { + for row in &mut self.tiles { + for tile in row { + tile.restart()?; + } + } + + Ok(()) + } } impl Drop for Multiview { fn drop(&mut self) { write!(self.stdout, "{}", cursor::Show).unwrap(); + + for row in &mut self.tiles { + for tile in row { + tile.kill().ok(); + } + } } } @@ -155,6 +178,12 @@ pub enum Msg { /// A click occured. Click(u16, u16), + /// Restarts the selected tile. + Restart, + + /// Restarts all tiles. + RestartAll, + /// Scroll up one line. ScrollUp, @@ -233,6 +262,8 @@ pub fn main() -> io::Result<()> { let evt = c.unwrap(); match evt { Event::Key(Key::Char('q')) => sender.send(Msg::Exit).unwrap(), + Event::Key(Key::Char('r')) => sender.send(Msg::Restart).unwrap(), + Event::Key(Key::Char('R')) => sender.send(Msg::RestartAll).unwrap(), Event::Key(Key::Down) => sender.send(Msg::ScrollDown).unwrap(), Event::Key(Key::Up) => sender.send(Msg::ScrollUp).unwrap(), Event::Key(Key::End) => sender.send(Msg::ScrollFullDown).unwrap(), @@ -255,6 +286,8 @@ pub fn main() -> io::Result<()> { Ok(Msg::Stderr(coords, line)) => multiview.push_stderr(coords, line), Ok(Msg::Click(x, y)) => multiview.select_tile((x, y), term_size), Ok(Msg::ScrollDown) => multiview.scroll_down(), + Ok(Msg::Restart) => multiview.restart()?, + Ok(Msg::RestartAll) => multiview.restart_all()?, Ok(Msg::ScrollUp) => multiview.scroll_up(), Ok(Msg::ScrollFullDown) => multiview.scroll_full_down(), Ok(Msg::ScrollFullUp) => multiview.scroll_full_up(), diff --git a/src/tile.rs b/src/tile.rs index 5a1cfa4..b4555e8 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,7 +1,7 @@ //! This module contains everything related to tiles. -use std::io::Read; -use std::process::Stdio; +use std::io::{self, Read}; +use std::process::{Child, Stdio}; use std::sync::mpsc::Sender; use std::thread; @@ -96,12 +96,12 @@ impl TileBuilder { number_lines: 1, counting: true, column_number: 0, + child: None, }) } } /// A tile with a command running inside it. -#[derive(Debug)] pub struct Tile { /// The command that should be executed in the tile. pub command: Vec, @@ -145,6 +145,9 @@ pub struct Tile { /// The number of the current column. pub column_number: u16, + + /// The PTY and the child process of the command running in the tile. + pub child: Option<(Pty, Child)>, } impl Tile { @@ -161,83 +164,63 @@ impl Tile { let size = self.inner_size; let sender = self.sender.clone(); - thread::spawn(move || { - let pty = Pty::new().unwrap(); - pty.resize(pty_process::Size::new(size.1, size.0)).unwrap(); + let pty = Pty::new().unwrap(); + pty.resize(pty_process::Size::new(size.1, size.0)).unwrap(); - let mut child = Command::new(&clone[0]) - .args(&clone[1..]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(&pty.pts().unwrap()) - .unwrap(); + let mut child = Command::new(&clone[0]) + .args(&clone[1..]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(&pty.pts().unwrap()) + .unwrap(); - let mut stdout = child.stdout.take().unwrap(); - let mut stderr = child.stderr.take().unwrap(); - let stderr_sender = sender.clone(); + let mut stdout = child.stdout.take().unwrap(); + let mut stderr = child.stderr.take().unwrap(); + let stderr_sender = sender.clone(); - let coords = coords; + let coords = coords; - thread::spawn(move || loop { - let mut buffer = [0; 4096]; - let result = stderr.read(&mut buffer); + thread::spawn(move || loop { + let mut buffer = [0; 4096]; + let result = stdout.read(&mut buffer); - match result { - Ok(0) => break, + match result { + Ok(0) => break, - Ok(n) => { - stderr_sender - .send(Msg::Stderr( - coords, - String::from_utf8_lossy(&buffer[0..n]).to_string(), - )) - .unwrap(); - } - - Err(_) => break, + Ok(n) => { + sender + .send(Msg::Stderr( + coords, + String::from_utf8_lossy(&buffer[0..n]).to_string(), + )) + .unwrap(); } - }); - loop { - let mut buffer = [0; 4096]; - let result = stdout.read(&mut buffer); - - match result { - Ok(0) => break, - - Ok(n) => { - sender - .send(Msg::Stderr( - coords, - String::from_utf8_lossy(&buffer[0..n]).to_string(), - )) - .unwrap(); - } - - Err(_) => break, - } + Err(_) => break, } - - sender - .send(Msg::Stdout(coords, String::from("\n"))) - .unwrap(); - - let code = child.wait().unwrap().code().unwrap(); - - let exit_string = format!( - "{}{}Command exited with return code {}{}\n", - style::Bold, - if code == 0 { - color::Green.fg_str() - } else { - color::Red.fg_str() - }, - code, - style::Reset, - ); - - sender.send(Msg::Stdout(coords, exit_string)).unwrap(); }); + + thread::spawn(move || loop { + let mut buffer = [0; 4096]; + let result = stderr.read(&mut buffer); + + match result { + Ok(0) => break, + + Ok(n) => { + stderr_sender + .send(Msg::Stderr( + coords, + String::from_utf8_lossy(&buffer[0..n]).to_string(), + )) + .unwrap(); + } + + Err(_) => break, + } + }); + + self.child = Some((pty, child)); } /// Push content into the stdout of the tile. @@ -456,4 +439,20 @@ impl Tile { self.scroll = 0; } } + + /// Kill the child command. + pub fn kill(&mut self) -> io::Result<()> { + if let Some((_, child)) = self.child.as_mut() { + child.kill()?; + } + + Ok(()) + } + + /// Restarts the child command. + pub fn restart(&mut self) -> io::Result<()> { + self.kill()?; + self.start(); + Ok(()) + } }