Skip to main content

kitty_graphics_protocol/
types.rs

1//! Type definitions for the Kitty graphics protocol
2
3use std::fmt;
4
5/// Image format for pixel data transmission
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[repr(u8)]
8#[derive(Default)]
9pub enum ImageFormat {
10    /// 24-bit RGB format (3 bytes per pixel)
11    Rgb = 24,
12    /// 32-bit RGBA format (4 bytes per pixel, default)
13    #[default]
14    Rgba = 32,
15    /// PNG format (compressed image data)
16    Png = 100,
17}
18
19impl fmt::Display for ImageFormat {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "{}", *self as u8)
22    }
23}
24
25/// Transmission medium for image data
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
27pub enum TransmissionMedium {
28    /// Direct transmission within the escape code itself (default)
29    #[default]
30    Direct,
31    /// Read from a file (regular files only)
32    File,
33    /// Read from a temporary file (terminal will delete after reading)
34    TempFile,
35    /// Read from a shared memory object
36    SharedMemory,
37}
38
39impl fmt::Display for TransmissionMedium {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let c = match self {
42            Self::Direct => 'd',
43            Self::File => 'f',
44            Self::TempFile => 't',
45            Self::SharedMemory => 's',
46        };
47        write!(f, "{c}")
48    }
49}
50
51/// Action to perform with the graphics command
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
53pub enum Action {
54    /// Query support and transmission medium availability
55    Query,
56    /// Transmit image data only (don't display)
57    Transmit,
58    /// Transmit and display the image (a=T)
59    #[default]
60    TransmitAndDisplay,
61    /// Display a previously transmitted image (a=p)
62    Place,
63    /// Delete images (a=d)
64    Delete,
65    /// Transmit animation frame data (a=f)
66    Frame,
67    /// Control animation (a=a)
68    AnimationControl,
69    /// Compose animation frames (a=c)
70    ComposeFrame,
71}
72
73impl fmt::Display for Action {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        let s = match self {
76            Self::Query => "q",
77            Self::Transmit => "t",
78            Self::TransmitAndDisplay => "T",
79            Self::Place => "p",
80            Self::Delete => "d",
81            Self::Frame => "f",
82            Self::AnimationControl => "a",
83            Self::ComposeFrame => "c",
84        };
85        write!(f, "{s}")
86    }
87}
88
89/// Delete target specification
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub enum DeleteTarget {
92    /// Delete all visible placements (a/A)
93    All,
94    /// Delete all visible placements and free image data (A)
95    AllWithFree,
96    /// Delete by image ID (i/I)
97    ById { free_data: bool },
98    /// Delete by image number (n/N)
99    ByNumber { free_data: bool },
100    /// Delete at cursor position (c/C)
101    AtCursor { free_data: bool },
102    /// Delete animation frames (f/F)
103    Frames { free_data: bool },
104    /// Delete at specific cell (p/P)
105    AtCell { free_data: bool },
106    /// Delete at cell with z-index (q/Q)
107    AtCellWithZIndex { free_data: bool },
108    /// Delete by ID range (r/R)
109    ByIdRange { free_data: bool },
110    /// Delete by column (x/X)
111    ByColumn { free_data: bool },
112    /// Delete by row (y/Y)
113    ByRow { free_data: bool },
114    /// Delete by z-index (z/Z)
115    ByZIndex { free_data: bool },
116}
117
118impl DeleteTarget {
119    /// Get the character code for this delete target
120    pub fn code(&self) -> char {
121        match self {
122            Self::All => 'a',
123            Self::AllWithFree => 'A',
124            Self::ById { free_data: false } => 'i',
125            Self::ById { free_data: true } => 'I',
126            Self::ByNumber { free_data: false } => 'n',
127            Self::ByNumber { free_data: true } => 'N',
128            Self::AtCursor { free_data: false } => 'c',
129            Self::AtCursor { free_data: true } => 'C',
130            Self::Frames { free_data: false } => 'f',
131            Self::Frames { free_data: true } => 'F',
132            Self::AtCell { free_data: false } => 'p',
133            Self::AtCell { free_data: true } => 'P',
134            Self::AtCellWithZIndex { free_data: false } => 'q',
135            Self::AtCellWithZIndex { free_data: true } => 'Q',
136            Self::ByIdRange { free_data: false } => 'r',
137            Self::ByIdRange { free_data: true } => 'R',
138            Self::ByColumn { free_data: false } => 'x',
139            Self::ByColumn { free_data: true } => 'X',
140            Self::ByRow { free_data: false } => 'y',
141            Self::ByRow { free_data: true } => 'Y',
142            Self::ByZIndex { free_data: false } => 'z',
143            Self::ByZIndex { free_data: true } => 'Z',
144        }
145    }
146}
147
148/// Animation control commands
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub enum AnimationControl {
151    /// Stop the animation
152    Stop,
153    /// Run in loading mode (wait for more frames at end)
154    Loading,
155    /// Run normally (loop at end)
156    Run,
157}
158
159impl fmt::Display for AnimationControl {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        let n = match self {
162            Self::Stop => 1,
163            Self::Loading => 2,
164            Self::Run => 3,
165        };
166        write!(f, "{n}")
167    }
168}
169
170/// Composition mode for frame operations
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
172pub enum CompositionMode {
173    /// Alpha blend (default)
174    #[default]
175    AlphaBlend,
176    /// Simple pixel replacement
177    Replace,
178}
179
180impl fmt::Display for CompositionMode {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        let n = match self {
183            Self::AlphaBlend => 0,
184            Self::Replace => 1,
185        };
186        write!(f, "{n}")
187    }
188}
189
190/// Frame composition parameters for a=c action
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct FrameComposition {
193    /// Source frame number (1-based)
194    pub source_frame: u32,
195    /// Destination frame number (1-based)
196    pub dest_frame: u32,
197    /// Rectangle width in pixels
198    pub width: Option<u32>,
199    /// Rectangle height in pixels
200    pub height: Option<u32>,
201    /// Source X offset
202    pub source_x: Option<u32>,
203    /// Source Y offset
204    pub source_y: Option<u32>,
205    /// Destination X offset
206    pub dest_x: Option<u32>,
207    /// Destination Y offset
208    pub dest_y: Option<u32>,
209    /// Composition mode
210    pub mode: CompositionMode,
211}
212
213impl Default for FrameComposition {
214    fn default() -> Self {
215        Self {
216            source_frame: 1,
217            dest_frame: 1,
218            width: None,
219            height: None,
220            source_x: None,
221            source_y: None,
222            dest_x: None,
223            dest_y: None,
224            mode: CompositionMode::AlphaBlend,
225        }
226    }
227}
228
229/// Unicode placeholder configuration for virtual placements
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub struct UnicodePlaceholder {
232    /// Number of columns for the placeholder
233    pub columns: u16,
234    /// Number of rows for the placeholder
235    pub rows: u16,
236}
237
238/// Compression algorithm
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240pub enum Compression {
241    /// ZLIB deflate compression (RFC 1950)
242    Zlib,
243}
244
245impl fmt::Display for Compression {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        let s = match self {
248            Self::Zlib => "z",
249        };
250        write!(f, "{s}")
251    }
252}
253
254/// Cursor movement policy after placing an image
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
256pub enum CursorPolicy {
257    /// Default: move cursor right by columns and down by rows
258    #[default]
259    Default,
260    /// Don't move the cursor
261    NoMove,
262}
263
264impl fmt::Display for CursorPolicy {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        let n = match self {
267            Self::Default => 0,
268            Self::NoMove => 1,
269        };
270        write!(f, "{n}")
271    }
272}