repo_description: A new way to see and navigate directory trees : https://dystroy.org/broot
repo_topics: balanced-bfs-descent command-line command-line-tool fuzzy-search hacktoberfest linux rust tree
code: mod detect_support;
  mod image_renderer;
  mod terminal_esc;
  
  pub use image_renderer::*;
  
  use crate::display::cell_size_in_pixels;
  
  use {
      crate::{
          app::AppContext,
          display::W,
          errors::ProgramError,
          image::SourceImage,
          kitty::detect_support::is_tmux,
      },
      crokey::crossterm::style::Color,
      once_cell::sync::Lazy,
      std::{
          io::Write,
          path::Path,
          sync::Mutex,
      },
      termimad::Area,
  };
  
  pub type KittyImageId = usize;
  
  static MANAGER: Lazy<Mutex<KittyManager>> = Lazy::new(|| {
      let manager = KittyManager {
          rendered_images: Vec::new(),
          renderer: MaybeRenderer::Untested,
      };
      Mutex::new(manager)
  });
  
  pub fn manager() -> &'static Mutex<KittyManager> {
      &MANAGER
  }
  
  #[derive(Debug)]
  pub struct KittyManager {
      rendered_images: Vec<RenderedImage>,
      renderer: MaybeRenderer,
  }
  
  #[derive(Debug)]
  struct RenderedImage {
      image_id: KittyImageId,
      drawing_count: usize,
  }
  
  #[derive(Debug)]
  enum MaybeRenderer {
      Untested,
      Disabled,
      Enabled { renderer: KittyImageRenderer },
  }
  
  impl KittyManager {
      /// return the renderer if it's already checked and enabled, none if
      /// it's disabled or if it hasn't been tested yet
      pub fn renderer_if_tested(&mut self) -> Option<&mut KittyImageRenderer> {
          match &mut self.renderer {
              MaybeRenderer::Enabled { renderer } => Some(renderer),
              _ => None,
          }
      }
      pub fn delete_temp_files(&mut self) {
          if let MaybeRenderer::Enabled { renderer } = &mut self.renderer {
              renderer.delete_temp_files();
          }
      }
      pub fn renderer(
          &mut self,
          con: &AppContext,
      ) -> Option<&mut KittyImageRenderer> {
          if matches!(self.renderer, MaybeRenderer::Disabled) {
              return None;
          }
          if matches!(self.renderer, MaybeRenderer::Enabled { .. }) {
              return self.renderer_if_tested();
          }
          let options = KittyImageRendererOptions {
              display: con.kitty_graphics_display,
              transmission_medium: con.kitty_graphics_transmission,
              kept_temp_files: con.kept_kitty_temp_files,
              is_tmux: is_tmux(),
          };
          match KittyImageRenderer::new(options) {
              Some(renderer) => {
                  self.renderer = MaybeRenderer::Enabled { renderer };
                  self.renderer_if_tested()
              }
              None => {
                  self.renderer = MaybeRenderer::Disabled;
                  None
              }
          }
      }
      pub fn keep(
          &mut self,
          kept_id: KittyImageId,
          drawing_count: usize,
      ) {
          for image in &mut self.rendered_images {
              if image.image_id == kept_id {
                  image.drawing_count = drawing_count;
              }
          }
      }
      #[allow(clippy::too_many_arguments)] // yes, I know
      pub fn try_print_image(
          &mut self,
          w: &mut W,
          src: &SourceImage,
          src_path: &Path, // used to build a cache key
          area: &Area,
          bg: Color,
          drawing_count: usize,
          con: &AppContext,
      ) -> Result<Option<KittyImageId>, ProgramError> {
          if let Some(renderer) = self.renderer(con) {
              let (cell_width, cell_height) = cell_size_in_pixels()?;
              let area_width = area.width as u32 * cell_width;
              let area_height = area.height as u32 * cell_height;
              let img = src.fitting(area_width, area_height, None)?;
              let new_id = renderer.print(w, &img, src_path, area, bg)?;
              self.rendered_images.push(RenderedImage {
                  image_id: new_id,
                  drawing_count,
              });
              Ok(Some(new_id))
          } else {
              Ok(None)
          }
      }
      pub fn erase_images_before(
          &mut self,
          w: &mut W,
          drawing_count: usize,
      ) -> Result<(), ProgramError> {
          let mut kept_images = Vec::new();
          let is_tmux = detect_support::is_tmux();
          let tmux_nest_count = if is_tmux {
              detect_support::get_tmux_nest_count()
          } else {
              0
          };
          let tmux_header = is_tmux.then_some(terminal_esc::get_tmux_header(tmux_nest_count));
          let tmux_tail = is_tmux.then_some(terminal_esc::get_tmux_tail(tmux_nest_count));
          let esc = terminal_esc::get_esc_seq(tmux_nest_count);
          for image in self.rendered_images.drain(..) {
              if image.drawing_count >= drawing_count {
                  kept_images.push(image);
              } else {
                  let id = image.image_id;
                  debug!("erase kitty image {id}");
                  if let Some(s) = &tmux_header {
                      write!(w, "{s}")?;
                  }
                  write!(w, "{}_Ga=d,d=I,i={}{}\\", &esc, id, &esc)?;
                  if let Some(s) = &tmux_tail {
                      write!(w, "{s}")?;
                  }
              }
          }
          self.rendered_images = kept_images;
          Ok(())
      }
  }
  