From fc063efd4299df6bfccf5bbaa6e5fadb106e5bdc Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Mon, 9 Jun 2025 21:45:02 -0600 Subject: [PATCH] fall back to stdout if shms don't work --- benches/utils.rs | 4 +++- src/converter.rs | 45 ++++++++++++++++++++------------------------- src/kitty.rs | 32 +++++++++++++++++++++++++++++--- src/main.rs | 13 +++++++++++-- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/benches/utils.rs b/benches/utils.rs index 7267bad..e3785bd 100644 --- a/benches/utils.rs +++ b/benches/utils.rs @@ -122,7 +122,9 @@ pub fn start_converting_loop( to_main_tx, from_main_rx, picker, - prerender + prerender, + // just assume shms work for now, who cares + true )); let from_converter_rx = from_converter_rx.into_stream(); diff --git a/src/converter.rs b/src/converter.rs index 1f281a6..b87c94d 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -52,7 +52,8 @@ pub async fn run_conversion_loop( sender: Sender>, receiver: Receiver, mut picker: Picker, - prerender: usize + prerender: usize, + shms_work: bool ) -> Result<(), SendError>> { let mut images = vec![]; let mut page: usize = 0; @@ -64,7 +65,8 @@ pub async fn run_conversion_loop( page: usize, iteration: &mut usize, prerender: usize, - pid: u32 + pid: u32, + shms_work: bool ) -> Result, RenderError> { if images.is_empty() || *iteration >= prerender { return Ok(None); @@ -117,10 +119,6 @@ pub async fn run_conversion_loop( y: 0 }; - // We don't actually want to Crop this image, but we've already - // verified (with the ImageSurface stuff) that the image is the correct - // size for the area given, so to save ratatui the work of having to - // resize it, we tell them to crop it to fit. let txt_img = match picker.protocol_type() { ProtocolType::Kitty => { let area = ratatui_image::protocol::ImageSource::round_pixel_size_to_cells( @@ -129,24 +127,20 @@ pub async fn run_conversion_loop( picker.font_size() ); - match kittage::image::Image::shm_from( - dyn_img, - &format!("__tdf_kittage_{pid}_page_{page_num}") - ) { - Ok(mut img) => { - img.num_or_id = - NumberOrId::Id(NonZeroU32::new(page_num as u32 + 1).unwrap()); - ConvertedImage::Kitty { - img: MaybeTransferred::NotYet(img), - area - } - } - // todo: fallback to non-shm image here without cloning dyn_img above - // Err(_) => ConvertedImage::Kitty(dyn_img.into()) - Err(e) => - return Err(RenderError::Converting(format!( - "Couldn't write to shm: {e}" - ))), + let mut img = if shms_work { + kittage::image::Image::shm_from( + dyn_img, + &format!("__tdf_kittage_{pid}_page_{page_num}") + ) + .map_err(|e| RenderError::Converting(format!("Couldn't write to shm: {e}")))? + } else { + kittage::image::Image::from(dyn_img) + }; + + img.num_or_id = NumberOrId::Id(NonZeroU32::new(page_num as u32 + 1).unwrap()); + ConvertedImage::Kitty { + img: MaybeTransferred::NotYet(img), + area } } _ => ConvertedImage::Generic( @@ -203,7 +197,8 @@ pub async fn run_conversion_loop( page, &mut iteration, prerender, - pid + pid, + shms_work ) { Ok(None) => break, Ok(Some(img)) => sender.send(Ok(img))?, diff --git a/src/kitty.rs b/src/kitty.rs index a5ce954..6be9672 100644 --- a/src/kitty.rs +++ b/src/kitty.rs @@ -1,8 +1,14 @@ -use std::io::Write; +use std::{io::Write, num::NonZeroU32}; -use crossterm::{cursor::MoveTo, event::EventStream, execute}; +use crossterm::{ + cursor::MoveTo, + event::EventStream, + execute, + terminal::{disable_raw_mode, enable_raw_mode} +}; +use image::DynamicImage; use kittage::{ - AsyncInputReader, ImageDimensions, ImageId, PixelFormat, + AsyncInputReader, ImageDimensions, ImageId, NumberOrId, PixelFormat, action::Action, delete::{ClearOrDelete, DeleteConfig, WhichToDelete}, display::DisplayConfig, @@ -55,6 +61,26 @@ pub async fn run_action<'image, 'data, 'es>( .map(|(_, i)| i) } +pub async fn do_shms_work(ev_stream: &mut EventStream) -> bool { + let img = DynamicImage::new_rgb8(1, 1); + let pid = std::process::id(); + let Ok(mut k_img) = kittage::image::Image::shm_from(img, &format!("__tdf_kittage_test_{pid}")) + else { + return false; + }; + + // apparently the terminal won't respond to queries unless they have an Id instead of a number + k_img.num_or_id = NumberOrId::Id(NonZeroU32::new(u32::MAX).unwrap()); + + enable_raw_mode().unwrap(); + + let res = run_action(Action::Query(&k_img), ev_stream).await; + + disable_raw_mode().unwrap(); + + res.is_ok() +} + pub async fn display_kitty_images<'es>( images: Vec<(usize, &mut MaybeTransferred, Rect)>, ev_stream: &'es mut EventStream diff --git a/src/main.rs b/src/main.rs index 9e9fb37..95a6c81 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ use ratatui_image::picker::{Picker, ProtocolType}; use tdf::{ PrerenderLimit, converter::{ConvertedPage, ConverterMsg, run_conversion_loop}, - kitty::{display_kitty_images, run_action}, + kitty::{display_kitty_images, do_shms_work, run_action}, renderer::{self, RenderError, RenderInfo, RenderNotif}, tui::{BottomMessage, InputAction, MessageSetting, Tui} }; @@ -254,7 +254,12 @@ async fn main() -> Result<(), WrappedErr> { let (to_main, from_converter) = flume::unbounded(); let is_kitty = picker.protocol_type() == ProtocolType::Kitty; - tokio::spawn(run_conversion_loop(to_main, from_main, picker, 20)); + + let shms_work = is_kitty && do_shms_work(&mut ev_stream).await; + + tokio::spawn(run_conversion_loop( + to_main, from_main, picker, 20, shms_work + )); let file_name = path.file_name().map_or_else( || "Unknown file".into(), @@ -430,6 +435,10 @@ async fn enter_redraw_loop( // This is the error that kitty & ghostty provide us when they delete an // image due to memory constraints, so if we get it, we just fix it by // re-rendering so it don't display it to the user + // + // [TODO] maybe when we detect that an image was deleted, we probe the + // terminal for the pages around it to see if they were deleted too and if + // they were, we re-render them? idk TransmitError::Terminal(TerminalError::NoEntity(_)) => (), _ => tui.set_msg(MessageSetting::Some(BottomMessage::Error(format!( "{err_desc}: {enum_err}"