1pub mod protocol;
7pub mod videotex;
8
9use crate::{
10 protocol::{
11 Baudrate, FunctionMode, Pro1, Pro2, Pro2Resp, Pro3, Pro3Resp, Protocol, Rom, RoutingRx,
12 RoutingTx,
13 },
14 videotex::{C0, C1, G2},
15};
16
17use videotex::{FunctionKey, SIChar, UserInput, G0};
18
19use std::io::{Error, ErrorKind, Result};
20
21pub trait IntoSequence<const N: usize> {
24 fn sequence(self) -> [u8; N];
26}
27
28impl<T, const N: usize> IntoSequence<N> for &[T; N]
29where
30 T: Into<u8> + Copy,
31{
32 fn sequence(self) -> [u8; N] {
33 self.map(|x| x.into())
34 }
35}
36
37pub trait MinitelRead {
39 fn read(&mut self, data: &mut [u8]) -> Result<()>;
41}
42
43pub trait MinitelWrite {
45 fn send(&mut self, data: &[u8]) -> Result<()>;
47 fn flush(&mut self) -> Result<()>;
49}
50
51pub trait MinitelBaudrateControl {
53 fn set_baudrate(&mut self, baudrate: Baudrate) -> Result<()>;
55}
56
57pub struct Minitel<S> {
66 pub port: S,
67}
68
69impl<S> Minitel<S> {
70 pub fn new(port: S) -> Self {
71 Self { port }
72 }
73}
74
75impl<S: MinitelRead + MinitelWrite> Minitel<S> {
76 #[inline(always)]
77 pub fn clear_screen(&mut self) -> Result<()> {
78 self.write_byte(C0::FF)
79 }
80
81 #[inline(always)]
82 pub fn read_rom(&mut self) -> Result<Rom> {
83 self.pro1(Pro1::EnqRom)?;
84 self.wait_for(C0::SOH)?;
85 let mut rom = [0; 3];
86 self.read_bytes(&mut rom)?;
87 self.expect_read(C0::EOL)?;
88 Ok(rom.into())
89 }
90
91 #[inline(always)]
92 pub fn get_pos(&mut self) -> Result<(u8, u8)> {
93 self.c1(C1::EnqCursor)?;
94 self.wait_for(C0::US)?;
95 let mut position = [0; 2];
96 self.read_bytes(&mut position)?;
97 Ok((position[1] - 0x40 - 1, position[0] - 0x40 - 1))
98 }
99
100 #[inline(always)]
101 pub fn set_function_mode(&mut self, mode: FunctionMode, enable: bool) -> Result<()> {
102 let start_stop = if enable { Pro2::Start } else { Pro2::Stop };
103 self.pro2(start_stop, mode)?;
104 let _status = self.read_pro2(Pro2Resp::RepStatus)?;
105 Ok(())
106 }
107
108 #[inline(always)]
109 pub fn set_routing(
110 &mut self,
111 enable: bool,
112 recepter: RoutingRx,
113 emitter: RoutingTx,
114 ) -> Result<()> {
115 let cmd = if enable {
116 Pro3::RoutingOn
117 } else {
118 Pro3::RoutingOff
119 };
120 self.pro3(cmd, recepter, emitter)?;
121 let (_recepter, _status) = self.read_pro3(Pro3Resp::RoutingFrom)?;
122 Ok(())
123 }
124
125 #[inline(always)]
126 pub fn get_speed(&mut self) -> Result<Baudrate> {
127 self.pro1(Pro1::EnqSpeed)?;
128 let code = self.read_pro2(Pro2Resp::QuerySpeedAnswer)?;
129 Baudrate::try_from(code).map_err(|_| ErrorKind::InvalidData.into())
130 }
131
132 #[inline(always)]
133 pub fn set_rouleau(&mut self, enable: bool) -> Result<()> {
134 self.set_function_mode(FunctionMode::Rouleau, enable)
135 }
136
137 #[inline(always)]
138 pub fn set_procedure(&mut self, enable: bool) -> Result<()> {
139 self.set_function_mode(FunctionMode::Procedure, enable)
140 }
141
142 #[inline(always)]
143 pub fn set_minuscule(&mut self, enable: bool) -> Result<()> {
144 self.set_function_mode(FunctionMode::Minuscule, enable)
145 }
146}
147
148impl<S: MinitelRead> Minitel<S> {
149 #[inline(always)]
151 pub fn read_bytes(&mut self, data: &mut [u8]) -> Result<()> {
152 self.port.read(data)
153 }
154
155 #[inline(always)]
157 pub fn read_byte(&mut self) -> Result<u8> {
158 let mut data = [0];
159 self.read_bytes(&mut data)?;
160 Ok(data[0])
161 }
162
163 pub fn read_s0_stroke(&mut self) -> Result<UserInput> {
167 let b = self.read_byte()?;
168 if let Ok(g0) = G0::try_from(b) {
169 return Ok(UserInput::Char(g0.into()));
170 }
171 let c0 = C0::try_from(b).map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
172 match c0 {
173 C0::ESC => {
174 let c1 = C1::try_from(self.read_byte()?)
176 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
177 Ok(UserInput::C1(c1))
178 }
179 C0::Sep => {
180 let fct = FunctionKey::try_from(self.read_byte()?)
182 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
183 Ok(UserInput::FunctionKey(fct))
184 }
185 C0::SS2 => {
186 let g2 = G2::try_from(self.read_byte()?)
188 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
189
190 if let Some(diacritics) = g2.unicode_diacritic() {
191 let char: char = self.read_byte()?.into();
193 let char = unicode_normalization::char::compose(char, diacritics).ok_or(
194 Error::new(ErrorKind::InvalidData, "Invalid diacritic composition"),
195 )?;
196 Ok(UserInput::Char(char))
197 } else {
198 Ok(UserInput::Char(g2.char()))
200 }
201 }
202 _ => Ok(UserInput::C0(c0)),
203 }
204 }
205
206 #[inline(always)]
207 pub fn wait_for(&mut self, byte: impl Into<u8> + Copy) -> Result<()> {
208 for _ in 0..10 {
209 if self.read_byte()? == byte.into() {
210 return Ok(());
211 }
212 }
213 Err(ErrorKind::TimedOut.into())
214 }
215
216 #[inline(always)]
217 pub fn expect_read(&mut self, byte: impl Into<u8> + Copy) -> Result<()> {
218 let got = self.read_byte()?;
219 if got != byte.into() {
220 return Err(ErrorKind::InvalidData.into());
221 }
222 Ok(())
223 }
224
225 #[inline(always)]
226 pub fn read_pro2(&mut self, expected_ack: Pro2Resp) -> Result<u8> {
227 self.wait_for(C0::ESC)?;
228 self.expect_read(Protocol::Pro2)?;
229 self.expect_read(expected_ack)?;
230 self.read_byte()
231 }
232
233 #[inline(always)]
234 pub fn read_pro3(&mut self, expected_ack: Pro3Resp) -> Result<(u8, u8)> {
235 self.wait_for(C0::ESC)?;
236 self.expect_read(Protocol::Pro3)?;
237 self.expect_read(expected_ack)?;
238 Ok((self.read_byte()?, self.read_byte()?))
239 }
240}
241
242impl<S: MinitelWrite> Minitel<S> {
243 #[inline(always)]
245 pub fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
246 self.port.send(data)
247 }
248
249 #[inline(always)]
251 pub fn write_byte<T: Into<u8> + Copy>(&mut self, byte: T) -> Result<()> {
252 self.write_bytes(&[byte.into()])
253 }
254
255 #[inline(always)]
257 pub fn flush(&mut self) -> Result<()> {
258 self.port.flush()
259 }
260
261 #[inline(always)]
262 pub fn write_sequence<const N: usize>(&mut self, sequence: impl IntoSequence<N>) -> Result<()> {
263 self.write_bytes(sequence.sequence().as_ref())
264 }
265
266 #[inline(always)]
267 pub fn c1(&mut self, c1: C1) -> Result<()> {
268 self.write_sequence(c1)
269 }
270
271 #[inline(always)]
272 pub fn write_g2(&mut self, g2: G2) -> Result<()> {
273 self.write_sequence(g2)
274 }
275
276 #[inline(always)]
277 pub fn show_cursor(&mut self) -> Result<()> {
278 self.write_byte(C0::Con)
279 }
280
281 #[inline(always)]
282 pub fn hide_cursor(&mut self) -> Result<()> {
283 self.write_byte(C0::Coff)
284 }
285
286 #[inline(always)]
287 pub fn set_pos(&mut self, x: u8, y: u8) -> Result<()> {
288 self.write_bytes(&[C0::US.into(), 0x40 + y, 0x40 + x + 1]) }
290
291 #[inline(always)]
292 pub fn cursor_down(&mut self) -> Result<()> {
293 self.write_bytes(&[C0::LF.into()])
294 }
295
296 #[inline(always)]
297 pub fn cursor_up(&mut self) -> Result<()> {
298 self.write_byte(C0::VT)
299 }
300
301 #[inline(always)]
302 pub fn cursor_right(&mut self) -> Result<()> {
303 self.write_byte(C0::HT)
304 }
305
306 #[inline(always)]
307 pub fn cursor_left(&mut self) -> Result<()> {
308 self.write_byte(C0::BS)
309 }
310
311 #[inline(always)]
312 pub fn start_zone(&mut self, funcs: &[C1]) -> Result<()> {
313 for func in funcs {
314 self.c1(*func)?;
315 }
316 self.zone_delimiter()?;
317 Ok(())
318 }
319
320 #[inline(always)]
321 pub fn zone_delimiter(&mut self) -> Result<()> {
322 self.write_byte(0x20)
323 }
324
325 pub fn write_char(&mut self, c: char) -> Result<()> {
326 if let Ok(c) = SIChar::try_from(c) {
327 self.si_char(c)?;
328 return Ok(());
329 }
330 Err(ErrorKind::InvalidData.into())
331 }
332
333 pub fn si_char(&mut self, c: SIChar) -> Result<()> {
334 match c {
335 SIChar::G0(g0) => self.write_byte(g0)?,
336 SIChar::G0Diacritic(g0, g2) => {
337 self.write_g2(g2)?;
338 self.write_byte(g0)?;
339 }
340 SIChar::G2(g2) => self.write_g2(g2)?,
341 }
342 Ok(())
343 }
344
345 #[inline(always)]
346 pub fn write_str(&mut self, s: &str) -> Result<()> {
347 for c in s.chars() {
348 self.write_char(c)?;
349 }
350 Ok(())
351 }
352
353 #[inline(always)]
354 pub fn writeln(&mut self, s: &str) -> Result<()> {
355 let mut s = s.to_string();
356 s.push_str("\r\n");
357 self.write_str(&s)
358 }
359
360 #[inline(always)]
362 pub fn pro1(&mut self, x: Pro1) -> Result<()> {
363 self.write_bytes(&Protocol::pro1(x))?;
364 Ok(())
365 }
366
367 #[inline(always)]
369 pub fn pro2(&mut self, x: Pro2, y: impl Into<u8> + Copy) -> Result<()> {
370 self.write_bytes(&Protocol::pro2(x, y))?;
371 Ok(())
372 }
373
374 #[inline(always)]
376 pub fn pro3(
377 &mut self,
378 x: Pro3,
379 y: impl Into<u8> + Copy,
380 z: impl Into<u8> + Copy,
381 ) -> Result<()> {
382 self.write_bytes(&Protocol::pro3(x, y, z))?;
383 Ok(())
384 }
385}
386
387impl<S: MinitelRead + MinitelWrite + MinitelBaudrateControl> Minitel<S> {
389 pub fn search_speed(&mut self) -> Result<Baudrate> {
390 for baudrate in [
391 Baudrate::B1200,
392 Baudrate::B300,
393 Baudrate::B4800,
394 Baudrate::B9600,
395 ] {
396 log::debug!("Trying baudrate: {}", baudrate);
397 self.port.flush()?;
398 self.port.set_baudrate(baudrate)?;
399 if self.get_speed().is_ok() {
401 log::debug!("Found baudrate: {}", baudrate);
402 return Ok(baudrate);
403 }
404 }
405 Err(ErrorKind::NotFound.into())
406 }
407
408 #[inline(always)]
409 pub fn set_speed(&mut self, baudrate: Baudrate) -> Result<Baudrate> {
410 self.pro2(Pro2::Prog, baudrate.code())?;
411 self.port.flush()?;
412 self.port.set_baudrate(baudrate)?;
413
414 let speed_code = self.read_pro2(Pro2Resp::QuerySpeedAnswer)?;
415 let baudrate = Baudrate::try_from(speed_code).map_err(|_| ErrorKind::InvalidData)?;
416 Ok(baudrate)
417 }
418}
419
420impl<T> MinitelRead for T
421where
422 T: std::io::Read,
423{
424 fn read(&mut self, data: &mut [u8]) -> Result<()> {
425 self.read_exact(data)
426 }
427}
428
429impl<T> MinitelWrite for T
430where
431 T: std::io::Write,
432{
433 fn send(&mut self, data: &[u8]) -> Result<()> {
434 self.write_all(data)
435 }
436
437 fn flush(&mut self) -> Result<()> {
438 self.flush()
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 #[test]
446 pub fn read_stroke() {
447 let seq: Vec<_> = "He?! ".bytes().collect();
448 let mut minitel = Minitel::new(std::io::Cursor::new(seq));
449 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('H'));
450 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('e'));
451 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('?'));
452 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('!'));
453 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char(' '));
454
455 let seq: Vec<_> = vec![0x20, 0x13, 0x41, 0x13, 0x49, 0x20, 0x1B, 0x54];
456 let mut minitel = Minitel::new(std::io::Cursor::new(seq));
457 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char(' '));
458 assert_eq!(
459 minitel.read_s0_stroke().unwrap(),
460 UserInput::FunctionKey(FunctionKey::Envoi)
461 );
462 assert_eq!(
463 minitel.read_s0_stroke().unwrap(),
464 UserInput::FunctionKey(FunctionKey::ConnexionFin)
465 );
466 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char(' '));
467 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::C1(C1::BgBlue));
468
469 let seq: Vec<_> = vec![0x19, 0x42, 0x65, 0x19, 0x3D]; let mut minitel = Minitel::new(std::io::Cursor::new(seq));
471 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('é'));
472 assert_eq!(minitel.read_s0_stroke().unwrap(), UserInput::Char('½'));
473 }
474
475 #[test]
476 pub fn write_str() {
477 let seq: Vec<u8> = Vec::new();
478 let mut minitel = Minitel::new(std::io::Cursor::new(seq));
479 minitel.write_str("Hé½").unwrap();
480 let written = minitel.port.into_inner();
481 assert_eq!(written, vec![0x48, 0x19, 0x42, 0x65, 0x19, 0x3D]); }
483}