Adds support for kill and restart

This commit is contained in:
Thomas Forgione 2023-10-25 14:44:30 +02:00
parent 692d38869a
commit 18bc80ee8e
2 changed files with 102 additions and 70 deletions

View File

@ -136,11 +136,34 @@ impl<W: Write> Multiview<W> {
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<W: Write> Drop for Multiview<W> {
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(),

View File

@ -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<String>,
@ -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(())
}
}