1use std::error::Error;
35use std::fmt::{Display, Formatter};
36use std::io;
37use std::io::{BufRead, BufReader, Write};
38use std::net::TcpStream;
39use std::process::{Command, Stdio};
40
41#[derive(Debug)]
45pub struct TevClient {
46 socket: TcpStream,
47}
48
49#[derive(Debug)]
54pub enum TevError {
55 Command { io: std::io::Error },
57 Stdout { io: std::io::Error },
59 NoSocketResponse { read: String },
62 TcpConnect { host: String, io: std::io::Error },
65 IO { io: std::io::Error },
67}
68
69impl TevClient {
70 pub fn wrap(socket: TcpStream) -> Self {
83 TevClient { socket }
84 }
85
86 pub fn spawn_path_default() -> Result<TevClient, TevError> {
88 TevClient::spawn(Command::new("tev"))
89 }
90
91 pub fn spawn(mut command: Command) -> Result<TevClient, TevError> {
105 const PATTERNS: &[&str] = &[
106 "Initialized IPC, listening on ",
107 "Connected to primary instance at ",
108 ];
109
110 let mut child = command.stdout(Stdio::piped()).spawn()
111 .map_err(|io| TevError::Command { io })?;
112 let reader = BufReader::new(child.stdout.take().unwrap());
113
114 let mut read = String::new();
115 for line in reader.lines() {
116 let line = line.map_err(|io| TevError::Stdout { io })?;
117
118 for pattern in PATTERNS {
119 if let Some(start) = line.find(pattern) {
120 let rest = &line[start + pattern.len()..];
121
122 let end = rest.find('\u{1b}').unwrap_or(rest.len());
124 let host = &rest[..end];
125
126 let socket = TcpStream::connect(host)
127 .map_err(|io| TevError::TcpConnect { host: host.to_string(), io })?;
128 return Ok(TevClient::wrap(socket));
129 }
130 }
131
132 read.push_str(&line);
133 read.push('\n');
134 }
135
136 Err(TevError::NoSocketResponse { read })
137 }
138
139 pub fn send(&mut self, packet: impl TevPacket) -> io::Result<()> {
151 let vec = vec![0, 0, 0, 0];
153
154 let mut target = TevWriter { target: vec };
156 packet.write_to(&mut target);
157 let mut vec = target.target;
158
159 let packet_length = vec.len() as u32;
161 vec[0..4].copy_from_slice(&packet_length.to_le_bytes());
162
163 self.socket.write_all(&vec)
164 }
165}
166
167#[derive(Debug)]
169pub struct PacketOpenImage<'a> {
170 pub image_name: &'a str,
171 pub grab_focus: bool,
172 pub channel_selector: &'a str,
173}
174
175impl TevPacket for PacketOpenImage<'_> {
176 fn write_to(&self, writer: &mut TevWriter) {
177 writer.write(PacketType::OpenImageV2);
178 writer.write(self.grab_focus);
179 writer.write(self.image_name);
180 writer.write(self.channel_selector);
181 }
182}
183
184#[derive(Debug)]
186pub struct PacketReloadImage<'a> {
187 pub image_name: &'a str,
188 pub grab_focus: bool,
189}
190
191impl TevPacket for PacketReloadImage<'_> {
192 fn write_to(&self, writer: &mut TevWriter) {
193 writer.write(PacketType::ReloadImage);
194 writer.write(self.grab_focus);
195 writer.write(self.image_name);
196 }
197}
198
199#[derive(Debug)]
201pub struct PacketUpdateImage<'a, S: AsRef<str> + 'a> {
202 pub image_name: &'a str,
203 pub grab_focus: bool,
204 pub channel_names: &'a [S],
205 pub channel_offsets: &'a [u64],
206 pub channel_strides: &'a [u64],
207 pub x: u32,
208 pub y: u32,
209 pub width: u32,
210 pub height: u32,
211 pub data: &'a [f32],
212}
213
214impl<'a, S: AsRef<str> + 'a> TevPacket for PacketUpdateImage<'a, S> {
215 fn write_to(&self, writer: &mut TevWriter) {
216 let channel_count = self.channel_names.len();
217
218 assert_ne!(channel_count, 0, "Must update at least one channel");
219 assert_eq!(channel_count, self.channel_offsets.len(), "Channel count must be consistent");
220 assert_eq!(channel_count, self.channel_strides.len(), "Channel count must be consistent");
221
222 let pixel_count = (self.width as u64) * (self.height as u64);
223 assert_ne!(pixel_count, 0, "Must update at least one pixel");
224
225 let max_data_index_used = self.channel_offsets.iter().zip(self.channel_strides)
226 .map(|(&o, &s)| o + (pixel_count - 1) * s)
227 .max().unwrap();
228 assert_eq!(max_data_index_used + 1, self.data.len() as u64, "Data size does not match actually used data range");
229
230 writer.write(PacketType::UpdateImageV3);
231 writer.write(self.grab_focus);
232 writer.write(self.image_name);
233 writer.write(channel_count as u32);
234 writer.write_all(self.channel_names.iter().map(AsRef::as_ref));
235 writer.write(self.x);
236 writer.write(self.y);
237 writer.write(self.width);
238 writer.write(self.height);
239 writer.write_all(self.channel_offsets);
240 writer.write_all(self.channel_strides);
241
242 writer.write_all(self.data)
243 }
244}
245
246#[derive(Debug)]
248pub struct PacketCloseImage<'a> {
249 pub image_name: &'a str,
250}
251
252impl TevPacket for PacketCloseImage<'_> {
253 fn write_to(&self, writer: &mut TevWriter) {
254 writer.write(PacketType::CloseImage);
255 writer.write(self.image_name);
256 }
257}
258
259#[derive(Debug)]
261pub struct PacketCreateImage<'a, S: AsRef<str> + 'a> {
262 pub image_name: &'a str,
263 pub grab_focus: bool,
264 pub width: u32,
265 pub height: u32,
266 pub channel_names: &'a [S],
267}
268
269impl<'a, S: AsRef<str> + 'a> TevPacket for PacketCreateImage<'a, S> {
270 fn write_to(&self, writer: &mut TevWriter) {
271 writer.write(PacketType::CreateImage);
272 writer.write(self.grab_focus);
273 writer.write(self.image_name);
274 writer.write(self.width);
275 writer.write(self.height);
276 writer.write(self.channel_names.len() as u32);
277 writer.write_all(self.channel_names.iter().map(AsRef::as_ref));
278 }
279}
280
281#[doc(hidden)]
283pub struct TevWriter {
284 target: Vec<u8>,
285}
286
287#[repr(C)]
288#[derive(Debug, Copy, Clone)]
289enum PacketType {
290 ReloadImage = 1,
291 CloseImage = 2,
292 CreateImage = 4,
293 UpdateImageV3 = 6,
294 OpenImageV2 = 7,
295}
296
297impl TevWriter {
298 fn write(&mut self, value: impl TevWritable) {
299 value.write_to(self);
300 }
301
302 fn write_all(&mut self, values: impl IntoIterator<Item=impl TevWritable>) {
303 for value in values {
304 value.write_to(self);
305 }
306 }
307}
308
309#[doc(hidden)]
311pub trait TevPacket {
312 fn write_to(&self, writer: &mut TevWriter);
313}
314
315trait TevWritable {
316 fn write_to(self, writer: &mut TevWriter);
317}
318
319impl<T: TevWritable + Copy> TevWritable for &T {
320 fn write_to(self, writer: &mut TevWriter) {
321 (*self).write_to(writer);
322 }
323}
324
325impl TevWritable for bool {
326 fn write_to(self, writer: &mut TevWriter) {
327 writer.target.push(self as u8);
328 }
329}
330
331impl TevWritable for PacketType {
332 fn write_to(self, writer: &mut TevWriter) {
333 writer.target.push(self as u8);
334 }
335}
336
337impl TevWritable for u32 {
338 fn write_to(self, writer: &mut TevWriter) {
339 writer.target.extend_from_slice(&self.to_le_bytes());
340 }
341}
342
343impl TevWritable for u64 {
344 fn write_to(self, writer: &mut TevWriter) {
345 writer.target.extend_from_slice(&self.to_le_bytes());
346 }
347}
348
349impl TevWritable for f32 {
350 fn write_to(self, writer: &mut TevWriter) {
351 writer.target.extend_from_slice(&self.to_le_bytes());
352 }
353}
354
355impl TevWritable for &'_ str {
356 fn write_to(self, writer: &mut TevWriter) {
357 assert!(!self.contains('\0'), "cannot send strings containing '\\0'");
358 writer.target.extend_from_slice(self.as_bytes());
359 writer.target.push(0);
360 }
361}
362
363impl From<std::io::Error> for TevError {
364 fn from(io: std::io::Error) -> Self {
365 TevError::IO { io }
366 }
367}
368
369impl Display for TevError {
370 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
371 match self {
372 TevError::Command { io } =>
373 write!(f, "error during command execution: {}", io),
374 TevError::Stdout { io } =>
375 write!(f, "error during stdout reading: {}", io),
376 TevError::NoSocketResponse { read } =>
377 write!(f, "stdout did not contain socket, got '{}'", read),
378 TevError::TcpConnect { host, io } =>
379 write!(f, "error during attempted tcp connection to '{}': {}", host, io),
380 TevError::IO { io } =>
381 write!(f, "generic IO error: {}", io),
382 }
383 }
384}
385
386impl std::error::Error for TevError {
387 fn source(&self) -> Option<&(dyn Error + 'static)> {
388 match self {
389 TevError::Command { io } | TevError::Stdout { io } |
390 TevError::TcpConnect { host: _, io } | TevError::IO { io } =>
391 Some(io),
392 TevError::NoSocketResponse { read: _ } =>
393 None,
394 }
395 }
396}