kittyfb 0.1.0

renders pictures to a kitty terminal
Documentation
use io::Write;
use std::io;

unsafe fn write_to_shm(buf: &[u8], cstr: &std::ffi::CString, nbytes: usize) -> bool {
    use libc::{
        ftruncate, mmap, munmap, shm_open, shm_unlink, MAP_FAILED, MAP_SHARED, O_CREAT, O_RDWR,
        PROT_READ, PROT_WRITE,
    };
    use std::ptr::{copy_nonoverlapping, null_mut};

    shm_unlink(cstr.as_ptr());
    let fd = shm_open(cstr.as_ptr(), O_RDWR | O_CREAT, 0777);
    if fd == -1 {
        eprintln!(">:(1 {}", *libc::__errno_location());
        return false;
    }

    let res = ftruncate(fd, nbytes as i64);
    if res == -1 {
        eprintln!(">:(2");
        shm_unlink(cstr.as_ptr());
        return false;
    }

    let fb = mmap(
        null_mut(),
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        fd,
        0,
    );
    if fb == MAP_FAILED {
        eprintln!(">:(3");
        shm_unlink(cstr.as_ptr());
        return false;
    }

    copy_nonoverlapping(buf as *const [u8] as *const u8, fb as *mut u8, nbytes);

    let unmap_res = munmap(fb, nbytes);
    if unmap_res == -1 {
        eprintln!(">:(4");
        shm_unlink(cstr.as_ptr());
        return false;
    }

    return true;
}

pub fn display_shm(buf: &[u8], size: (usize, usize), bits_per_pixel: usize) -> io::Result<()> {
    use rand::Rng;

    let mut rng = rand::thread_rng();
    let chrs = "abcdefghijklmnopqrstuwvxyz0123456789";
    let salt: String = (0..48)
        .map(|_| rng.gen_range(0usize, chrs.len()))
        .map(|i| chrs.as_bytes()[i] as char)
        .collect();
    let name = format!("/kittyfb{}", salt);

    let cstr = std::ffi::CString::new(name.as_bytes()).unwrap();
    let nbytes = size.0 * size.1 * bits_per_pixel / 8;
    let shm_ok = unsafe { write_to_shm(buf, &cstr, nbytes) };

    if shm_ok {
        let cmd = format!(
            "\x1b_Gf={},s={},v={},a=T,t=s;",
            bits_per_pixel, size.0, size.1
        );
        let payload = base64::encode(cstr.into_bytes());

        let stdout = std::io::stdout();
        let mut handle = stdout.lock();
        handle.write_all(&cmd.as_bytes())?;
        handle.write_all(&payload.as_bytes())?;
        handle.write_all(b"\x1b\\")?;
        handle.flush()?;

        Ok(())
    } else {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "couldn't create a shared memory object",
        ))
    }
}