cursive_image/kitty/
image.rs1use super::{super::image::*, command::*};
2
3use {
4 crossterm::{ExecutableCommand, cursor::*},
5 cursive::*,
6 std::{io, sync::atomic::*},
7};
8
9static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
10
11impl Drop for Image {
12 fn drop(&mut self) {
13 if let Some(id) = self.get_id() {
14 _ = delete(id);
15 }
16 }
17}
18
19pub trait KittyImage {
25 fn allocate(&self) -> io::Result<usize>;
27
28 #[allow(unused)]
30 fn free(&self) -> io::Result<()>;
31
32 fn display<PositionT, SizeT>(
34 &self,
35 position: PositionT,
36 size: Option<SizeT>,
37 window: Option<Rect>,
38 ) -> io::Result<()>
39 where
40 PositionT: Into<Vec2>,
41 SizeT: Into<Vec2>;
42}
43
44impl KittyImage for Image {
45 fn allocate(&self) -> io::Result<usize> {
46 Ok(match self.get_id() {
47 Some(id) => id,
48
49 None => {
50 let id = new_id();
51 self.set_id(Some(id));
52
53 let mut command = Command::default().with('f', self.format).with('i', id);
54
55 if self.format != ImageFormat::PNG {
56 command.add('s', self.size.x);
58 command.add('v', self.size.y);
59 }
60
61 match &self.source {
62 ImageSource::Owned(data, compressed) => {
63 if *compressed {
64 command.add('o', 'z');
65 }
66 command.execute_with_payload(data.as_slice())?;
67 }
68
69 ImageSource::LocalFile(path, compressed) => {
70 if *compressed {
71 command.add('o', 'z');
72 }
73 command.add('t', 'f');
74 command.execute_with_path_payload(path)?;
75 }
76
77 ImageSource::Stream(stream) => {
78 let (reader, compressed) = stream.open()?;
79 if compressed {
80 command.add('o', 'z');
81 }
82 command.execute_with_payload(reader)?;
83 }
84 };
85
86 id
87 }
88 })
89 }
90
91 fn free(&self) -> io::Result<()> {
92 self.set_placement(None);
93 if let Some(id) = self.get_id() {
94 delete(id)?;
95 self.set_id(None);
96 }
97 Ok(())
98 }
99
100 fn display<PositionT, SizeT>(
101 &self,
102 position: PositionT,
103 size: Option<SizeT>,
104 window: Option<Rect>,
105 ) -> io::Result<()>
106 where
107 PositionT: Into<Vec2>,
108 SizeT: Into<Vec2>,
109 {
110 let id = self.allocate()?;
111 let old_placement = self.get_placement();
112
113 let placement = new_id();
114 self.set_placement(Some(placement));
115
116 _ = move_cursor(position.into());
117
118 let mut command = Command::default().with('a', 'p').with('i', id).with('p', placement);
120
121 if let Some(size) = size {
122 let size = size.into();
123 command.add('c', size.x);
124 command.add('r', size.y);
125 }
126
127 if let Some(window) = window {
128 command.add('x', window.left());
129 command.add('y', window.top());
130 command.add('w', window.width());
131 command.add('h', window.height());
132 }
133
134 command.execute()?;
135
136 if let Some(old_placement) = old_placement {
139 Command::default().with('a', 'd').with('d', 'i').with('i', id).with('p', old_placement).execute()?;
140 }
141
142 Ok(())
143 }
144}
145
146fn new_id() -> usize {
147 NEXT_ID.fetch_add(1, Ordering::SeqCst)
148}
149
150fn move_cursor(position: Vec2) -> io::Result<()> {
151 io::stdout().execute(MoveTo(position.x as u16, position.y as u16))?;
152 Ok(())
153}
154
155fn delete(id: usize) -> io::Result<()> {
156 Command::default().with('a', 'd').with('d', 'I').with('i', id).execute()
157}