diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 19 | ||||
-rw-r--r-- | src/hbox.rs | 18 | ||||
-rw-r--r-- | src/hunter-media.rs | 365 | ||||
-rw-r--r-- | src/imgview.rs | 25 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/mediaview.rs | 41 |
6 files changed, 313 insertions, 161 deletions
diff --git a/src/config.rs b/src/config.rs index 5e24123..21d8404 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,7 +13,8 @@ use crate::fail::{HError, HResult, ErrorLog}; struct ArgvConfig { animation: Option<bool>, show_hidden: Option<bool>, - icons: Option<bool> + icons: Option<bool>, + sixel: Option<bool>, } impl ArgvConfig { @@ -21,7 +22,8 @@ impl ArgvConfig { ArgvConfig { animation: None, show_hidden: None, - icons: None + icons: None, + sixel: None } } } @@ -35,6 +37,7 @@ pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> { let animation = args.is_present("animation-off"); let show_hidden = args.is_present("show-hidden"); let icons = args.is_present("icons"); + let sixel = args.is_present("sixel"); let mut config = ArgvConfig::new(); @@ -50,6 +53,10 @@ pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> { config.icons = Some(true) } + if sixel == true { + config.sixel = Some(true) + } + *ARGV_CONFIG.write()? = config; Ok(()) } @@ -64,6 +71,7 @@ fn infuse_argv_config(mut config: Config) -> Config { argv_config.animation.map(|val| config.animation = val); argv_config.show_hidden.map(|val| config.show_hidden = val); argv_config.icons.map(|val| config.icons = val); + argv_config.sixel.map(|val| config.sixel = val); config } @@ -78,7 +86,8 @@ pub struct Config { pub media_autoplay: bool, pub media_mute: bool, pub media_previewer: String, - pub ratios: Vec::<usize> + pub ratios: Vec::<usize>, + pub sixel: bool, } @@ -99,7 +108,8 @@ impl Config { media_autoplay: false, media_mute: false, media_previewer: "hunter-media".to_string(), - ratios: vec![20,30,49] + ratios: vec![20,30,49], + sixel: false } } @@ -149,6 +159,7 @@ impl Config { } } } + Ok(("sixel", "off")) => { config.sixel = true; } _ => { HError::config_error::<Config>(line.to_string()).log(); } } config diff --git a/src/hbox.rs b/src/hbox.rs index 8ad23c6..e370fde 100644 --- a/src/hbox.rs +++ b/src/hbox.rs @@ -123,17 +123,23 @@ impl<T> HBox<T> where T: Widget + PartialEq { let len = coords.len(); let gap = if len == ratios.len() { 0 } else { 1 }; - let widget_xsize = *ratio as u16; + let prev_coords = coords.last(); + let prev_xsize = prev_coords.map(|c| c.xsize()); + let prev_xpos = prev_coords.map(|c| c.xpos()); + + let widget_xsize = box_xsize * *ratio as u16 / 100; + let widget_xpos = if len == 0 { box_coords.top().x() } else { - let prev_coords = coords.last().unwrap(); - let prev_xsize = prev_coords.xsize(); - let prev_xpos = prev_coords.position().x(); - - prev_xsize + prev_xpos + gap + prev_xsize.unwrap() + prev_xpos.unwrap() + gap }; + // Ensure that last widget isn't under/over sized due to gap/rounding + let widget_xsize = if len+1 == ratios.len() && len != 0 { + box_xsize - (prev_xpos.unwrap() + prev_xsize.unwrap()) + } else { widget_xsize }; + coords.push(Coordinates { size: Size((widget_xsize, box_ysize)), diff --git a/src/hunter-media.rs b/src/hunter-media.rs index 193dc06..cc1de41 100644 --- a/src/hunter-media.rs +++ b/src/hunter-media.rs @@ -1,7 +1,9 @@ // Based on https://github.com/jD91mZM2/termplay // MIT License -use image::{Pixel, FilterType, DynamicImage, GenericImageView}; +use image::{FilterType, DynamicImage, GenericImageView}; +use sixel::encoder::Encoder; +use base64; use termion::color::{Bg, Fg, Rgb}; #[cfg(feature = "video")] @@ -34,48 +36,61 @@ fn main() -> MResult<()> { .expect("provide ysize") .parse() .unwrap(); - #[cfg(feature = "video")] - let xpos = args.get(3) - .expect("provide xpos") + let xpix = args.get(3) + .expect("provide xsize in pixels") .parse::<usize>() .unwrap(); - #[cfg(feature = "video")] - let ypos = args.get(4) - .expect("provide ypos") + let ypix = args.get(4) + .expect("provide ysize in pixels") .parse::<usize>() .unwrap(); let preview_type = args.get(5) .expect("Provide preview type") .parse::<String>() .unwrap(); - #[cfg(feature = "video")] + // #[cfg(feature = "video")] let autoplay = args.get(6) .expect("Autoplay?") .parse::<bool>() .unwrap(); - #[cfg(feature = "video")] + // #[cfg(feature = "video")] let mute = args.get(7) .expect("Muted?") .parse::<bool>() .unwrap(); - let path = args.get(8).expect("Provide path"); + let sixel = args.get(8) + .expect("Use SIXEL?") + .parse::<bool>() + .unwrap(); + let path = args.get(9).expect("Provide path"); + let target = if sixel { + if std::env::var("TERM") == Ok(String::from("xterm-kitty")) { + RenderTarget::Kitty + } else { + RenderTarget::Sixel + } + } else { + RenderTarget::Unicode + }; + + + let renderer = Renderer::new(target, + xsize, + ysize, + xpix, + ypix); let result = match preview_type.as_ref() { #[cfg(feature = "video")] "video" => video_preview(path, - xsize, - ysize, - 0, - 0, + renderer, autoplay, - mute, - false), + mute), "image" => image_preview(path, - xsize, - ysize), + renderer), #[cfg(feature = "video")] "audio" => audio_preview(path, @@ -83,16 +98,7 @@ fn main() -> MResult<()> { mute), #[cfg(feature = "video")] - "video-raw" => video_preview(path, - xsize, - ysize, - xpos, - ypos, - autoplay, - mute, - true), - #[cfg(feature = "video")] - _ => { panic!("Available types: video(-raw)/image/audio") } + _ => { panic!("Available types: video/image/audio") } #[cfg(not(feature = "video"))] _ => { panic!("Available type: image") } @@ -107,15 +113,9 @@ fn main() -> MResult<()> { } fn image_preview(path: &str, - xsize: usize, - ysize: usize) -> MResult<()> { + renderer: Renderer) -> MResult<()> { let img = image::open(&path)?; - let renderer = Renderer::new(xsize, - ysize, - 0, - 0); - renderer.send_image(&img)?; Ok(()) @@ -123,23 +123,19 @@ fn image_preview(path: &str, #[cfg(feature = "video")] fn video_preview(path: &String, - xsize: usize, - ysize: usize, - xpos: usize, - ypos: usize, + renderer: Renderer, autoplay: bool, - mute: bool, - raw: bool) + mute: bool) -> MResult<()> { + let low_fps = renderer.target == RenderTarget::Sixel; - let (player, appsink) = make_gstreamer()?; + let (player, appsink) = make_gstreamer(low_fps)?; let uri = format!("file://{}", &path); player.set_property("uri", &uri)?; - let renderer = Renderer::new(xsize, ysize, xpos, ypos); let renderer = Arc::new(RwLock::new(renderer)); let crenderer = renderer.clone(); @@ -165,30 +161,24 @@ fn video_preview(path: &String, .map(|d| d.seconds().unwrap_or(0)) .unwrap_or(0); - if let Ok(mut renderer) = crenderer.write() { - match renderer.send_frame(&*sample, - position, - duration, - raw) { - Ok(()) => { - if autoplay == false { - // Just render first frame to get a static image - match p.set_state(gstreamer::State::Paused) - .into_result() { - Ok(_) => gstreamer::FlowReturn::Eos, - Err(_) => gstreamer::FlowReturn::Error - } - } else { - gstreamer::FlowReturn::Ok - } - } - Err(err) => { - println!("{:?}", err); - gstreamer::FlowReturn::Error + let renderer = crenderer.clone(); + std::thread::spawn(move || { + renderer.write() + .map(|mut r| r.send_frame(&*sample, + position, + duration)).ok() + }); + + if autoplay == false { + // Just render first frame to get a static image + match p.set_state(gstreamer::State::Paused) + .into_result() { + Ok(_) => gstreamer::FlowReturn::Eos, + Err(_) => gstreamer::FlowReturn::Error } - } - } else { gstreamer::FlowReturn::Error } - + } else { + gstreamer::FlowReturn::Ok + } } }) .eos({ @@ -276,6 +266,7 @@ fn read_keys(player: gstreamer::Element, "u" => { player.set_property("volume", &1.0)?; } + // TODO add pixel size "xy" => { if let Some(ref renderer) = renderer { let xsize = stdin.read_line()?; @@ -301,7 +292,7 @@ pub fn audio_preview(path: &String, autoplay: bool, mute: bool) -> MResult<()> { - let (player, _) = make_gstreamer()?; + let (player, _) = make_gstreamer(false)?; let uri = format!("file://{}", &path); @@ -333,7 +324,8 @@ pub fn audio_preview(path: &String, // MediaView needs empty line as separator writeln!(stdout, "")?; - // Send position and duration + // Send height, position and duration + writeln!(stdout, "0")?; writeln!(stdout, "{}", position)?; writeln!(stdout, "{}", duration)?; stdout.flush()?; @@ -353,8 +345,8 @@ pub fn audio_preview(path: &String, } #[cfg(feature = "video")] -pub fn make_gstreamer() -> MResult<(gstreamer::Element, - gstreamer_app::AppSink)> { +pub fn make_gstreamer(low_fps: bool) -> MResult<(gstreamer::Element, + gstreamer_app::AppSink)> { gstreamer::init()?; let player = gstreamer::ElementFactory::make("playbin", None) @@ -374,7 +366,11 @@ pub fn make_gstreamer() -> MResult<(gstreamer::Element, .unwrap(); - videorate.set_property("max-rate", &30)?; + if low_fps { + videorate.set_property("max-rate", &10)?; + } else { + videorate.set_property("max-rate", &30)?; + } let elems = &[&videorate, &pnmenc, &sink]; @@ -395,14 +391,20 @@ pub fn make_gstreamer() -> MResult<(gstreamer::Element, Ok((player, appsink)) } +#[derive(PartialEq)] +enum RenderTarget { + Unicode, + Sixel, + Kitty +} struct Renderer { + // encoder: RwLock<Encoder>, + target: RenderTarget, xsize: usize, ysize: usize, - #[cfg(feature = "video")] - xpos: usize, - #[cfg(feature = "video")] - ypos: usize, + xsize_pix: usize, + ysize_pix: usize, #[cfg(feature = "video")] last_frame: Option<DynamicImage>, #[cfg(feature = "video")] @@ -412,17 +414,26 @@ struct Renderer { } impl Renderer { - fn new(xsize: usize, + fn new(target: RenderTarget, + xsize: usize, ysize: usize, - xpos: usize, - ypos: usize) -> Renderer { + mut xsize_pix: usize, + mut ysize_pix: usize) -> Renderer { + + if std::env::var("TERM") == Ok(String::from("xterm")) + && target == RenderTarget::Sixel { + // xterm has a hard limit on graphics size + // maybe splitting the image into parts would work? + if xsize_pix > 1000 { xsize_pix = 1000 }; + if ysize_pix > 1000 { ysize_pix = 1000 }; + } + Renderer { + target, xsize, ysize, - #[cfg(feature = "video")] - xpos, - #[cfg(feature = "video")] - ypos, + xsize_pix, + ysize_pix, #[cfg(feature = "video")] last_frame: None, #[cfg(feature = "video")] @@ -432,7 +443,7 @@ impl Renderer { } } - + // TODO: Add pixel size #[cfg(feature = "video")] fn set_size(&mut self, xsize: usize, ysize: usize) -> MResult<()> { self.xsize = xsize; @@ -456,12 +467,10 @@ impl Renderer { } fn send_image(&self, image: &DynamicImage) -> MResult<()> { - let rendered_img = self.render_image(image); - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - - for line in rendered_img { - writeln!(stdout, "{}", line)?; + match self.target { + RenderTarget::Sixel => self.print_sixel(image)?, + RenderTarget::Unicode => self.print_unicode(image)?, + RenderTarget::Kitty => self.print_kitty(image)? } Ok(()) @@ -471,8 +480,7 @@ impl Renderer { fn send_frame(&mut self, frame: &gstreamer::sample::SampleRef, position: u64, - duration: u64, - raw: bool) + duration: u64) -> MResult<()> { let buffer = frame.get_buffer() .ok_or(format_err!("Couldn't get buffer from frame!"))?; @@ -482,50 +490,38 @@ impl Renderer { let stdout = std::io::stdout(); let mut stdout = stdout.lock(); + let img = image::load_from_memory_with_format(&map, + image::ImageFormat::PNM)?; + let (_, height) = self.max_size(&img); - if !raw { - let img = image::load_from_memory_with_format(&map, - image::ImageFormat::PNM)?; - let rendered_img = self.render_image(&img); - - self.last_frame = Some(img); - self.position = Some(position as usize); - self.duration = Some(duration as usize); - - for line in rendered_img { - writeln!(stdout, "{}", line)?; - } - } else { - stdout.write_all(map.as_slice())?; - - // Add newline after frame data - write!(stdout, "\n")?; + match self.target { + RenderTarget::Sixel => self.print_sixel(&img)?, + RenderTarget::Unicode => self.print_unicode(&img)?, + RenderTarget::Kitty => self.print_kitty(&img)? } + self.last_frame = Some(img); + self.position = Some(position as usize); + self.duration = Some(duration as usize); + // Empty line means end of frame writeln!(stdout, "")?; - // Send position and duration + // Send size (in rows), position and duration + writeln!(stdout, "{}", height)?; writeln!(stdout, "{}", position)?; writeln!(stdout, "{}", duration)?; - #[cfg(feature = "video")] - match raw { - true => { - writeln!(stdout, "{}", self.xpos)?; - writeln!(stdout, "{}", self.ypos)?; - } - _ => {} - } - Ok(()) } pub fn render_image(&self, image: &DynamicImage) -> Vec<String> { + use image::Pixel; let (xsize, ysize) = self.max_size(&image); + // double height, because of half-height unicode let img = image.resize_exact(xsize as u32, - ysize as u32, + ysize as u32 * 2, FilterType::Nearest).to_rgba(); @@ -553,37 +549,134 @@ impl Renderer { }).collect() } + fn print_unicode(&self, img: &DynamicImage) -> MResult<()> { + let rendered_img = self.render_image(img); + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + + for line in rendered_img { + writeln!(stdout, "{}", line)?; + } + + Ok(()) + } + + fn print_kitty(&self, img: &DynamicImage) -> MResult<()> { + let w = img.width(); + let h = img.height(); + + let (max_x, max_y) = self.max_size(img); + + let img = img.to_rgb().into_vec(); + + let mut file = std::fs::File::create("/tmp/img.raw").unwrap(); + file.write_all(&img)?; + // Necessary? + file.flush()?; + std::mem::drop(file); + + let path = base64::encode("/tmp/img.raw"); + + println!("\x1b_Gf=24,s={},v={},c={},r={},a=T,t=t;{}\x1b\\", + w, + h, + max_x, + max_y, + path); + + Ok(()) + } + + fn print_sixel(&self, img: &DynamicImage) -> MResult<()> { + use sixel::optflags::*; + + // Currently faster than covnerting/resizing using image... + img.save("/tmp/img.bmp")?; + + let encoder = Encoder::new().unwrap(); + + let (xpix, ypix) = self.max_size_pix(img); + + encoder.set_quality(Quality::Low).ok(); + encoder.set_encode_policy(EncodePolicy::Fast).ok(); + encoder.set_color_option(ColorOption::Builtin("xterm256")).ok(); + encoder.set_width(SizeSpecification::Pixel(xpix as u64)).ok(); + encoder.set_height(SizeSpecification::Pixel(ypix as u64)).ok(); + encoder.encode_file(&std::path::PathBuf::from("/tmp/img.bmp")).ok(); + + // End line printed by encoder + println!(""); + + Ok(()) + } + pub fn max_size(&self, image: &DynamicImage) -> (usize, usize) { + // TODO: cell_ratio = xpix / ypix! let xsize = self.xsize; let ysize = self.ysize; - let img_xsize = image.width(); + let img_xsize = image.width() * 2; // Cells are roughly 2:1 let img_ysize = image.height(); let img_ratio = img_xsize as f32 / img_ysize as f32; - let mut new_x = xsize; + let mut new_x; let mut new_y; - new_y = if img_ratio < 1 as f32 { - (xsize as f32 * img_ratio) as usize + if img_ratio < 1 as f32 { + new_x = (ysize as f32 * img_ratio) as usize; + new_y = ysize; } else { - (xsize as f32 / img_ratio) as usize - }; + new_x = xsize; + new_y = (xsize as f32 / img_ratio) as usize; + } - // Multiply by two because of half-block - if new_y > ysize*2 { - new_y = self.ysize * 2; + // ensure it fits within xsize + if new_x > xsize { + new_x = xsize; + new_y = (xsize as f32 / img_ratio) as usize; + } + + // ensure it fits within ysize + if new_y > ysize { + new_y = ysize; + new_x = (ysize as f32 * img_ratio) as usize; + } + + + (new_x as usize, new_y as usize) + } + + pub fn max_size_pix(&self, image: &DynamicImage) -> (usize, usize) + { + let xsize = self.xsize_pix; + let ysize = self.ysize_pix; + let img_xsize = image.width(); + let img_ysize = image.height(); + let img_ratio = img_xsize as f32 / img_ysize as f32; + + let mut new_x; + let mut new_y; + + // tall / slim + if img_ratio < 1 as f32 { + new_x = (ysize as f32 * img_ratio) as usize; + new_y = ysize; + // short / wide + } else { + new_x = xsize; + new_y = (xsize as f32 / img_ratio) as usize; + } - new_x = if img_ratio < 1 as f32 { - (ysize as f32 / img_ratio) as usize * 2 - } else { - (ysize as f32 * img_ratio) as usize * 2 - }; + // ensure it fits within xsize + if new_x > xsize { + new_x = xsize; + new_y = (xsize as f32 / img_ratio) as usize; } - // To make half-block encoding easier, y should be divisible by 2 - if new_y as u32 % 2 == 1 { - new_y += 1; + // ensure it fits within ysize + if new_y > ysize { + new_y = ysize; + new_x = (ysize as f32 * img_ratio) as usize; } diff --git a/src/imgview.rs b/src/imgview.rs index 93e1832..7534a7b 100644 --- a/src/imgview.rs +++ b/src/imgview.rs @@ -33,18 +33,25 @@ impl ImgView { pub fn encode_file(&mut self) -> HResult<()> { let (xsize, ysize) = self.core.coordinates.size_u(); - let (xpos, ypos) = self.core.coordinates.position_u(); + let (cols, rows) = termion::terminal_size()?; + let (xpix, ypix) = termion::terminal_size_pixels()?; + let (xpix, ypix) = (xpix/cols, ypix/rows); + let (xpix, ypix) = (xpix * (xsize as u16 + 1), + ypix * (ysize as u16 + 1)); + let file = &self.file; let media_previewer = self.core.config().media_previewer; + let sixel = self.core.config().sixel; let output = std::process::Command::new(&media_previewer) - .arg(format!("{}", (xsize))) + .arg(format!("{}", (xsize+1))) .arg(format!("{}", (ysize+1))) - .arg(format!("{}", xpos)) - .arg(format!("{}", ypos)) + .arg(format!("{}", xpix)) + .arg(format!("{}", ypix)) .arg("image") .arg(format!("true")) .arg(format!("true")) + .arg(format!("{}", sixel)) .arg(file.to_string_lossy().to_string()) .output() .map_err(|e| { @@ -111,7 +118,7 @@ impl Widget for ImgView { .iter() .enumerate() .fold(String::new(), |mut draw, (pos, line)| { - draw += &format!("{}", crate::term::goto_xy_u(xpos+1, + draw += &format!("{}", crate::term::goto_xy_u(xpos, ypos + pos)); draw += line; draw @@ -122,3 +129,11 @@ impl Widget for ImgView { Ok(draw) } } + +impl Drop for ImgView { + fn drop(&mut self) { + if self.core.config().sixel { + print!("\x1b_Ga=d\x1b\\"); + } + } +} diff --git a/src/main.rs b/src/main.rs index 7efbad7..959e5a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,6 +151,12 @@ fn parse_args() -> HResult<()> { .long("icons") .help("Show icons for different file types") .takes_value(false)) + .arg( + Arg::with_name("sixel") + .short("s") + .long("sixel") + .help("Show HQ graphics using sixel") + .takes_value(false)) // For "Add Action" action .arg( Arg::with_name("mime") diff --git a/src/mediaview.rs b/src/mediaview.rs index 2f9153c..6d3b1cc 100644 --- a/src/mediaview.rs +++ b/src/mediaview.rs @@ -45,6 +45,7 @@ pub struct MediaView { controller: Sender<String>, paused: bool, media_type: MediaType, + height: Arc<Mutex<usize>>, position: Arc<Mutex<usize>>, duration: Arc<Mutex<usize>>, stale: Stale, @@ -52,6 +53,7 @@ pub struct MediaView { preview_runner: Option<Box<dyn FnOnce(bool, bool, Arc<Mutex<usize>>, + Arc<Mutex<usize>>, Arc<Mutex<usize>>) -> HResult<()> + Send + 'static>> } @@ -90,7 +92,12 @@ impl MediaView { } let (xsize, ysize) = core.coordinates.size_u(); - let (xpos, ypos) = core.coordinates.position_u(); + let (cols, rows) = termion::terminal_size()?; + let (xpix, ypix) = termion::terminal_size_pixels()?; + let (xpix, ypix) = (xpix/cols, ypix/rows); + let (xpix, ypix) = (xpix * (xsize as u16 + 1), + ypix * (ysize as u16 - 1)); + let (tx_cmd, rx_cmd) = channel(); let imgview = ImgView { @@ -113,9 +120,11 @@ impl MediaView { let ctype = media_type.clone(); let ccore = core.clone(); let media_previewer = core.config().media_previewer; + let sixel = core.config().sixel; let run_preview = Box::new(move | auto, mute, + height: Arc<Mutex<usize>>, position: Arc<Mutex<usize>>, duration: Arc<Mutex<usize>>| -> HResult<()> { loop { @@ -125,18 +134,20 @@ impl MediaView { let mut previewer = std::process::Command::new(&media_previewer) - .arg(format!("{}", (xsize))) + .arg(format!("{}", (xsize+1))) // Leave space for position/seek bar .arg(format!("{}", (ysize-1))) - .arg(format!("{}", xpos)) - .arg(format!("{}", ypos)) + .arg(format!("{}", xpix)) + .arg(format!("{}", ypix)) .arg(format!("{}", ctype.to_str())) .arg(format!("{}", auto)) .arg(format!("{}", mute)) + .arg(format!("{}", sixel)) .arg(&path) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) + .stderr(std::process::Stdio::inherit()) + // .stderr(std::process::Stdio::piped()) .spawn() .map_err(|e| { let msg = format!("Couldn't run {}{}{}! Error: {:?}", @@ -187,6 +198,12 @@ impl MediaView { if line_buf == newline { line_buf.clear(); stdout.read_line(&mut line_buf)?; + let h = &line_buf.trim(); + *height.lock().unwrap() = h + .parse::<usize>()?; + + line_buf.clear(); + stdout.read_line(&mut line_buf)?; let pos = &line_buf.trim(); *position.lock().unwrap() = pos .parse::<usize>()?; @@ -225,6 +242,7 @@ impl MediaView { media_type: media_type, controller: tx_cmd, paused: false, + height: Arc::new(Mutex::new(0)), position: Arc::new(Mutex::new(0)), duration: Arc::new(Mutex::new(0)), stale: stale, @@ -240,6 +258,7 @@ impl MediaView { let stale = self.stale.clone(); let autoplay = self.autoplay(); let mute = self.mute(); + let height = self.height.clone(); let position = self.position.clone(); let duration = self.duration.clone(); let clear = self.get_core()?.get_clearlist()?; @@ -254,6 +273,7 @@ impl MediaView { runner.map(|runner| runner(autoplay, mute, + height, position, duration)); } @@ -470,19 +490,20 @@ impl Widget for MediaView { fn get_drawlist(&self) -> HResult<String> { let (xpos, ypos) = self.core.coordinates.position_u(); + let height = *self.height.lock()?; let progress_str = self.progress_string()?; let progress_bar = self.progress_bar()?; - let (frame, lines) = self.imgview + let frame= self.imgview .lock() - .map(|img| (img.get_drawlist(), img.lines()))?; + .map(|img| img.get_drawlist())?; let mut frame = frame?; - frame += &crate::term::goto_xy_u(xpos+1, ypos+lines); + frame += &crate::term::goto_xy_u(xpos, ypos+height); frame += &progress_str; - frame += &self.get_icons(lines)?; - frame += &crate::term::goto_xy_u(xpos+1, ypos+lines+1); + frame += &self.get_icons(height)?; + frame += &crate::term::goto_xy_u(xpos, ypos+height+1); frame += &progress_bar; Ok(frame) |