katui_present/surface/
full.rs1use crate::Sink;
5use std::io::{self, Write as _};
6
7use katui_proto::kitty::{Delete, ImageId as KittyImage, PlacementId as KittyPlacement};
8use katui_scene::placement::CellRect;
9use katui_scene::surface::Surface;
10
11use super::write_whole_image;
12
13#[derive(Debug)]
18pub struct KittySurface {
19 base: u32,
20 parity: bool,
21}
22
23impl KittySurface {
24 #[must_use]
27 pub fn new(base: u32) -> Self {
28 Self {
29 base,
30 parity: false,
31 }
32 }
33
34 pub fn render_frame<W: Sink>(
37 &mut self,
38 out: &mut W,
39 surface: &Surface,
40 area: CellRect,
41 ) -> io::Result<usize> {
42 let current = self.base + u32::from(self.parity);
43 let previous = self.base + u32::from(!self.parity);
44 let mut bytes = Vec::new();
45 bytes.write_all(b"\x1b[?2026h")?;
46 write_whole_image(&mut bytes, KittyImage(current), surface, area)?;
47 bytes.write_all(&Delete::Placement(KittyImage(previous), KittyPlacement(1)).encode())?;
48 bytes.write_all(b"\x1b[?2026l")?;
49 self.parity = !self.parity;
50 let written = bytes.len();
51 out.write_all(&bytes)?;
52 Ok(written)
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::KittySurface;
59 use crate::surface::test_support::solid;
60 use katui_scene::placement::CellRect;
61 use katui_scene::surface::Rgba;
62
63 fn area() -> CellRect {
64 CellRect {
65 x: 2,
66 y: 1,
67 columns: 20,
68 rows: 10,
69 }
70 }
71
72 #[test]
73 fn frames_alternate_ids_and_delete_the_previous_frame() {
74 let surface = solid(4, 4, Rgba::solid(255, 0, 0));
75 let mut presenter = KittySurface::new(90);
76 let mut first = Vec::new();
77 presenter
78 .render_frame(&mut first, &surface, area())
79 .expect("writing to a Vec cannot fail");
80 let text = String::from_utf8_lossy(&first);
81 assert!(text.contains("i=90,"), "the first frame uses the base id");
82 assert!(text.contains("d=i,i=91"), "the other id's placement clears");
83 assert!(text.contains("o=z"), "frames travel compressed");
84
85 let mut second = Vec::new();
86 presenter
87 .render_frame(&mut second, &surface, area())
88 .expect("writing to a Vec cannot fail");
89 let text = String::from_utf8_lossy(&second);
90 assert!(text.contains("i=91,"), "the second frame alternates");
91 assert!(
92 text.contains("d=i,i=90"),
93 "the first frame's placement clears"
94 );
95 }
96
97 #[test]
98 fn frames_stay_inside_synchronized_output() {
99 let surface = solid(2, 2, Rgba::solid(0, 255, 0));
100 let mut presenter = KittySurface::new(10);
101 let mut out = Vec::new();
102 presenter
103 .render_frame(&mut out, &surface, area())
104 .expect("writing to a Vec cannot fail");
105 let text = String::from_utf8_lossy(&out);
106 assert!(text.starts_with("\x1b[?2026h"));
107 assert!(text.ends_with("\x1b[?2026l"));
108 }
109}