cursive_image/image/
kitty.rs1use super::{super::kitty::*, image::*, source::*};
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 _ = release_image(id);
15 }
16 }
17}
18
19pub trait KittyImage {
25 fn register(&self) -> io::Result<usize>;
29
30 fn release(&self) -> io::Result<()>;
32
33 fn show<PositionT, SizeT>(&self, position: PositionT, size: Option<SizeT>, window: Option<Rect>) -> io::Result<()>
40 where
41 PositionT: Into<Vec2>,
42 SizeT: Into<Vec2>;
43
44 fn hide(&self) -> io::Result<()>;
46}
47
48impl KittyImage for Image {
49 fn register(&self) -> io::Result<usize> {
50 Ok(match self.get_id() {
51 Some(id) => id,
52
53 None => {
54 let id = new_id();
55 self.set_id(Some(id));
56
57 let mut command = Command::default().with('f', self.format).with('i', id);
58
59 if self.format.is_raw() {
60 command.add('s', self.size.x);
61 command.add('v', self.size.y);
62 }
63
64 match &self.source {
65 ImageSource::Owned(data, compressed) => {
66 if *compressed {
67 command.add('o', 'z');
68 }
69 command.execute_with_payload(data.as_slice())?;
70 }
71
72 ImageSource::LocalFile(path, compressed) => {
73 if *compressed {
74 command.add('o', 'z');
75 }
76 command.add('t', 'f');
77 command.execute_with_path_payload(path)?;
78 }
79
80 ImageSource::Stream(stream) => {
81 let (reader, compressed) = stream.open()?;
82 if compressed {
83 command.add('o', 'z');
84 }
85 command.execute_with_payload_from(reader)?;
86 }
87 };
88
89 id
90 }
91 })
92 }
93
94 fn release(&self) -> io::Result<()> {
95 self.set_placement(None);
96 if let Some(id) = self.get_id() {
97 release_image(id)?;
98 self.set_id(None);
99 }
100 Ok(())
101 }
102
103 fn show<PositionT, SizeT>(&self, position: PositionT, size: Option<SizeT>, window: Option<Rect>) -> io::Result<()>
104 where
105 PositionT: Into<Vec2>,
106 SizeT: Into<Vec2>,
107 {
108 let id = self.register()?;
109 let old_placement = self.get_placement();
110
111 let placement = new_id();
112 self.set_placement(Some(placement));
113
114 _ = move_cursor(position.into());
115
116 let mut command = Command::default().with('a', 'p').with('i', id).with('p', placement).with('q', 1);
118
119 if let Some(size) = size {
120 let size = size.into();
121 command.add('c', size.x);
122 command.add('r', size.y);
123 }
124
125 if let Some(window) = window {
126 command.add('x', window.left());
127 command.add('y', window.top());
128 command.add('w', window.width());
129 command.add('h', window.height());
130 }
131
132 command.execute()?;
133
134 if let Some(old_placement) = old_placement {
137 delete_placement(id, old_placement)?;
138 }
139
140 Ok(())
141 }
142
143 fn hide(&self) -> io::Result<()> {
144 if let Some(placement) = self.get_placement()
145 && let Some(id) = self.get_id()
146 {
147 delete_placement(id, placement)?;
148 self.set_placement(None);
149 }
150 Ok(())
151 }
152}
153
154fn new_id() -> usize {
155 NEXT_ID.fetch_add(1, Ordering::SeqCst)
156}
157
158fn move_cursor(position: Vec2) -> io::Result<()> {
159 io::stdout().execute(MoveTo(position.x as u16, position.y as u16))?;
160 Ok(())
161}
162
163fn release_image(id: usize) -> io::Result<()> {
164 Command::default().with('a', 'd').with('d', 'I').with('i', id).execute()
165}
166
167fn delete_placement(id: usize, placement: usize) -> io::Result<()> {
168 Command::default().with('a', 'd').with('d', 'i').with('i', id).with('p', placement).execute()
169}