Skip to content

Commit

Permalink
Explicit temp file handling for Kitty
Browse files Browse the repository at this point in the history
  • Loading branch information
atanunq committed Oct 13, 2024
1 parent 08b8f82 commit e754e4d
Showing 1 changed file with 20 additions and 18 deletions.
38 changes: 20 additions & 18 deletions src/printer/kitty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use console::{Key, Term};
use lazy_static::lazy_static;
use std::io::Write;
use std::io::{Error, ErrorKind};
use tempfile::NamedTempFile;

pub struct KittyPrinter;

Expand Down Expand Up @@ -74,15 +75,14 @@ fn has_local_support() -> ViuResult {
// create a temp file that will hold a 1x1 image
let x = image::RgbaImage::new(1, 1);
let raw_img = x.as_raw();
let path = store_in_tmp_file(raw_img)?;
let temp_file = store_in_tmp_file(raw_img)?;

// send the query
print!(
// t=t tells Kitty it's reading from a temp file and will delete if afterwards
// t=t tells Kitty it's reading from a temp file and will attempt to delete if afterwards
"\x1b_Gi=31,s=1,v=1,a=q,t=t;{}\x1b\\",
general_purpose::STANDARD.encode(path.to_str().ok_or_else(|| std::io::Error::new(
std::io::ErrorKind::Other,
"Could not convert path to &str"
general_purpose::STANDARD.encode(temp_file.path().to_str().ok_or_else(|| ViuError::Io(
Error::new(ErrorKind::Other, "Could not convert path to &str")
))?)
);
std::io::stdout().flush()?;
Expand All @@ -91,7 +91,6 @@ fn has_local_support() -> ViuResult {
let term = Term::stdout();
let mut response = Vec::new();

// TODO: could use a queue of length 3
while let Ok(key) = term.read_key() {
// The response will end with Esc('x1b'), followed by Backslash('\').
// Also, break if the Unknown key is found, which is returned when we're not in a tty
Expand All @@ -102,6 +101,9 @@ fn has_local_support() -> ViuResult {
}
}

// Explicitly clean up when finished with the file because destructor, OS and Kitty are not deterministic.
temp_file.close()?;

// Kitty response should end with these 3 Keys if it was successful
let expected = [
Key::Char('O'),
Expand All @@ -125,7 +127,7 @@ fn print_local(
) -> ViuResult<(u32, u32)> {
let rgba = img.to_rgba8();
let raw_img = rgba.as_raw();
let path = store_in_tmp_file(raw_img)?;
let temp_file = store_in_tmp_file(raw_img)?;

adjust_offset(stdout, config)?;

Expand All @@ -139,14 +141,16 @@ fn print_local(
img.height(),
w,
h,
general_purpose::STANDARD.encode(path.to_str().ok_or_else(|| ViuError::Io(Error::new(
ErrorKind::Other,
"Could not convert path to &str"
)))?)
general_purpose::STANDARD.encode(temp_file.path().to_str().ok_or_else(|| ViuError::Io(
Error::new(ErrorKind::Other, "Could not convert path to &str")
))?)
)?;
writeln!(stdout)?;
stdout.flush()?;

// Explicitly clean up when finished with the file because destructor, OS and Kitty are not deterministic.
temp_file.close()?;

Ok((w, h))
}

Expand Down Expand Up @@ -191,18 +195,16 @@ fn print_remote(
}

// Create a file in temporary dir and write the byte slice to it.
fn store_in_tmp_file(buf: &[u8]) -> std::result::Result<std::path::PathBuf, ViuError> {
let (mut tmpfile, path) = tempfile::Builder::new()
// The NamedTempFile will be deleted once it goes out of scope.
fn store_in_tmp_file(buf: &[u8]) -> std::result::Result<NamedTempFile, ViuError> {
let mut tmpfile = tempfile::Builder::new()
.prefix(TEMP_FILE_PREFIX)
.rand_bytes(1)
.tempfile()?
// Since the file is persisted, the user is responsible for deleting it afterwards. However,
// Kitty does this automatically after printing from a temp file.
.keep()?;
.tempfile()?;

tmpfile.write_all(buf)?;
tmpfile.flush()?;
Ok(path)
Ok(tmpfile)
}

#[cfg(test)]
Expand Down

0 comments on commit e754e4d

Please sign in to comment.