diff --git a/src/fail.rs b/src/fail.rs index cf5acab..4e793ac 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -185,6 +185,12 @@ impl HError { } +#[derive(Fail, Debug, Clone)] +pub enum ErrorCause { + #[fail(display = "{}", _0)] + Str(String) +} + lazy_static! { static ref LOG: Mutex> = Mutex::new(vec![]); diff --git a/src/imgview.rs b/src/imgview.rs index 8674a5b..6143add 100644 --- a/src/imgview.rs +++ b/src/imgview.rs @@ -1,22 +1,25 @@ +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::io::{BufReader, BufRead}; +use std::sync::atomic::{AtomicU32, Ordering}; + use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; -use crate::fail::HResult; - -use std::path::{Path, PathBuf}; - +use crate::fail::{HResult, ErrorCause}; use crate::mediaview::MediaError; -impl std::cmp::PartialEq for ImgView { - fn eq(&self, other: &Self) -> bool { - self.core == other.core && - self.buffer == other.buffer - } + + +lazy_static! { + static ref PID: AtomicU32 = AtomicU32::new(0); } +#[derive(Derivative)] +#[derivative(PartialEq)] pub struct ImgView { pub core: WidgetCore, pub buffer: Vec, - pub file: Option + pub file: Option, } impl ImgView { @@ -24,7 +27,7 @@ impl ImgView { let mut view = ImgView { core: core, buffer: vec![], - file: Some(file.to_path_buf()) + file: Some(file.to_path_buf()), }; view.encode_file()?; @@ -40,7 +43,7 @@ impl ImgView { let media_previewer = self.core.config().media_previewer; let g_mode = self.core.config().graphics; - let output = std::process::Command::new(&media_previewer) + let mut previewer = Command::new(&media_previewer) .arg(format!("{}", (xsize+1))) .arg(format!("{}", (ysize+1))) .arg(format!("{}", xpix)) @@ -51,7 +54,10 @@ impl ImgView { .arg(format!("true")) .arg(format!("{}", g_mode)) .arg(file.to_string_lossy().to_string()) - .output() + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() .map_err(|e| { let msg = format!("Couldn't run {}{}{}! Error: {:?}", crate::term::color_red(), @@ -62,14 +68,37 @@ impl ImgView { self.core.show_status(&msg).ok(); MediaError::NoPreviewer(msg) - })? - .stdout; + })?; + PID.store(previewer.id(), Ordering::Relaxed); - let output = std::str::from_utf8(&output)?; - let output = output.lines() - .map(|l| l.to_string()) - .collect(); + let stdout = previewer.stdout + .take() + .unwrap(); + + let output = BufReader::new(stdout) + .lines() + .collect::, _>>()?; + + let stderr = previewer.stderr + .take() + .unwrap(); + + let stderr = BufReader::new(stderr) + .lines() + .collect::>()?; + + let status = previewer.wait()?; + + PID.store(0, Ordering::Relaxed); + + if !status.success() { + match status.code() { + Some(code) => Err(MediaError::MediaViewerFailed(code, + ErrorCause::Str(stderr)))?, + None => Err(MediaError::MediaViewerKilled)? + } + } self.buffer = output; @@ -83,6 +112,20 @@ impl ImgView { pub fn lines(&self) -> usize { self.buffer.len() } + + pub fn kill_running() { + use nix::{unistd::Pid, + sys::signal::{kill, Signal}}; + + let pid = PID.load(Ordering::Relaxed); + + if pid == 0 { return; } + + let pid = Pid::from_raw(pid as i32); + kill(pid, Signal::SIGTERM).ok(); + + PID.store(0, Ordering::Relaxed); + } } diff --git a/src/mediaview.rs b/src/mediaview.rs index 8fb773a..e1dd7de 100644 --- a/src/mediaview.rs +++ b/src/mediaview.rs @@ -5,7 +5,7 @@ use failure::{self, Fail}; use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; use crate::async_value::Stale; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::fail::{HResult, HError, ErrorLog, ErrorCause}; use crate::imgview::ImgView; use std::path::{Path, PathBuf}; @@ -18,7 +18,13 @@ use std::process::Child; #[derive(Fail, Debug, Clone)] pub enum MediaError { #[fail(display = "{}", _0)] - NoPreviewer(String) + NoPreviewer(String), + #[fail(display = "No output could be read from {}", _0)] + NoOutput(String), + #[fail(display = "Media viewer exited with status code: {}", _0)] + MediaViewerFailed(i32, #[cause] ErrorCause), + #[fail(display = "Media viewer killed!")] + MediaViewerKilled, } impl From for HError { @@ -80,7 +86,7 @@ impl MediaView { let imgview = ImgView { core: core.clone(), buffer: vec![], - file: None + file: None, }; let (tx_cmd, rx_cmd) = channel(); diff --git a/src/preview.rs b/src/preview.rs index 2f1eebf..b31ee7c 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -23,6 +23,9 @@ lazy_static! { } fn kill_proc() -> HResult<()> { + // Kill media previewer if it still runs + ImgView::kill_running(); + let mut pid = SUBPROC.lock()?; pid.map(|pid| // Do this in another thread so we can wait on process to exit with SIGHUP @@ -34,9 +37,9 @@ fn kill_proc() -> HResult<()> { // Kill using process group, to clean up all child processes, too let pid = Pid::from_raw(pid as i32); - killpg(pid, Signal::SIGTERM).log(); + killpg(pid, Signal::SIGTERM).ok(); std::thread::sleep(sleep_time); - killpg(pid, Signal::SIGKILL).log(); + killpg(pid, Signal::SIGKILL).ok(); }) ); *pid = None;