use super::{super::kitty::*, image::*, source::*};
use {
crossterm::{ExecutableCommand, cursor::*},
cursive::*,
std::{io, sync::atomic::*},
};
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
impl Drop for Image {
fn drop(&mut self) {
if let Some(id) = self.get_id() {
_ = release_image(id);
}
}
}
pub trait KittyImage {
fn register(&self) -> io::Result<usize>;
fn release(&self) -> io::Result<()>;
fn show<PositionT, SizeT>(&self, position: PositionT, size: Option<SizeT>, window: Option<Rect>) -> io::Result<()>
where
PositionT: Into<Vec2>,
SizeT: Into<Vec2>;
fn hide(&self) -> io::Result<()>;
}
impl KittyImage for Image {
fn register(&self) -> io::Result<usize> {
Ok(match self.get_id() {
Some(id) => id,
None => {
let id = new_id();
self.set_id(Some(id));
let mut command = Command::default().with('f', self.format).with('i', id);
if self.format.is_raw() {
command.add('s', self.size.x);
command.add('v', self.size.y);
}
match &self.source {
ImageSource::Owned(data, compressed) => {
if *compressed {
command.add('o', 'z');
}
command.execute_with_payload(data.as_slice())?;
}
ImageSource::LocalFile(path, compressed) => {
if *compressed {
command.add('o', 'z');
}
command.add('t', 'f');
command.execute_with_path_payload(path)?;
}
ImageSource::Stream(stream) => {
let (reader, compressed) = stream.open()?;
if compressed {
command.add('o', 'z');
}
command.execute_with_payload_from(reader)?;
}
};
id
}
})
}
fn release(&self) -> io::Result<()> {
self.set_placement(None);
if let Some(id) = self.get_id() {
release_image(id)?;
self.set_id(None);
}
Ok(())
}
fn show<PositionT, SizeT>(&self, position: PositionT, size: Option<SizeT>, window: Option<Rect>) -> io::Result<()>
where
PositionT: Into<Vec2>,
SizeT: Into<Vec2>,
{
let id = self.register()?;
let old_placement = self.get_placement();
let placement = new_id();
self.set_placement(Some(placement));
_ = move_cursor(position.into());
let mut command = Command::default().with('a', 'p').with('i', id).with('p', placement).with('q', 1);
if let Some(size) = size {
let size = size.into();
command.add('c', size.x);
command.add('r', size.y);
}
if let Some(window) = window {
command.add('x', window.left());
command.add('y', window.top());
command.add('w', window.width());
command.add('h', window.height());
}
command.execute()?;
if let Some(old_placement) = old_placement {
delete_placement(id, old_placement)?;
}
Ok(())
}
fn hide(&self) -> io::Result<()> {
if let Some(placement) = self.get_placement()
&& let Some(id) = self.get_id()
{
delete_placement(id, placement)?;
self.set_placement(None);
}
Ok(())
}
}
fn new_id() -> usize {
NEXT_ID.fetch_add(1, Ordering::SeqCst)
}
fn move_cursor(position: Vec2) -> io::Result<()> {
io::stdout().execute(MoveTo(position.x as u16, position.y as u16))?;
Ok(())
}
fn release_image(id: usize) -> io::Result<()> {
Command::default().with('a', 'd').with('d', 'I').with('i', id).execute()
}
fn delete_placement(id: usize, placement: usize) -> io::Result<()> {
Command::default().with('a', 'd').with('d', 'i').with('i', id).with('p', placement).execute()
}