Skip to main content

rlvgl_playit/
command.rs

1//! Command types for the playit test driver.
2
3use rlvgl_core::event::{Event, Key, MAX_TOUCH_POINTS, TouchPoint, TouchState};
4
5/// A test automation command.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum Command<'a> {
8    /// Inject an event into the widget tree root.
9    Inject(EventSpec),
10    /// Inject an event targeted at a tagged widget.
11    InjectTagged(&'a str, EventSpec),
12    /// Query widget state by tag.
13    Query(QuerySpec<'a>),
14    /// Dump framebuffer pixels in a region.
15    DumpPixels(DumpSpec),
16    /// Request runtime telemetry / status.
17    Status,
18    /// Start the event recorder (clears buffer).
19    RecordStart,
20    /// Stop recording and dump all entries.
21    RecordStop,
22    /// Dump recorded entries without stopping.
23    RecordDump,
24    /// Application-defined extension command (raw payload after `X`).
25    Extension(&'a [u8]),
26}
27
28/// Specifies an event to inject.  Mirrors [`Event`] but is easier to
29/// serialize and does not carry multi-touch arrays.
30///
31/// Coordinate fields (`x`, `y`) are in the widget/landscape coordinate space.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[allow(missing_docs)]
34pub enum EventSpec {
35    /// Advance animations / timers.
36    Tick,
37    /// Debounced tap (primary action trigger).
38    PressRelease { x: i32, y: i32 },
39    /// Stable contact began.
40    PressDown { x: i32, y: i32 },
41    /// Raw pointer pressed.
42    PointerDown { x: i32, y: i32 },
43    /// Raw pointer released.
44    PointerUp { x: i32, y: i32 },
45    /// Pointer moved while pressed.
46    PointerMove { x: i32, y: i32 },
47    /// Two consecutive short taps.
48    DoubleTap { x: i32, y: i32 },
49    /// Keyboard key pressed.
50    KeyDown { key: KeySpec },
51    /// Keyboard key released.
52    KeyUp { key: KeySpec },
53    /// Multi-touch frame with per-point data.
54    Touch {
55        count: u8,
56        points: [TouchPointSpec; MAX_TOUCH_POINTS],
57    },
58}
59
60/// Per-contact state in a multi-touch frame.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[allow(missing_docs)]
63pub enum TouchStateSpec {
64    /// New contact this frame.
65    Down,
66    /// Contact lifted this frame.
67    Up,
68    /// Contact still held, possibly moved.
69    Contact,
70}
71
72/// One contact point in a multi-touch frame.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct TouchPointSpec {
75    /// Touch tracking ID (0–4).
76    pub id: u8,
77    /// Per-point event flag.
78    pub state: TouchStateSpec,
79    /// Horizontal coordinate.
80    pub x: i32,
81    /// Vertical coordinate.
82    pub y: i32,
83}
84
85impl TouchStateSpec {
86    /// Convert to the core [`TouchState`].
87    pub fn to_core(self) -> TouchState {
88        match self {
89            Self::Down => TouchState::Down,
90            Self::Up => TouchState::Up,
91            Self::Contact => TouchState::Contact,
92        }
93    }
94}
95
96impl Default for TouchPointSpec {
97    fn default() -> Self {
98        Self {
99            id: 0,
100            state: TouchStateSpec::Up,
101            x: 0,
102            y: 0,
103        }
104    }
105}
106
107/// Serializable key identifier, parallel to [`Key`].
108///
109/// Variant names match the core [`Key`] enum.
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111#[allow(missing_docs)]
112pub enum KeySpec {
113    Escape,
114    Enter,
115    Space,
116    ArrowUp,
117    ArrowDown,
118    ArrowLeft,
119    ArrowRight,
120    Function(u8),
121    Character(char),
122    Other(u32),
123}
124
125/// What to query about a tagged widget.
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub enum QuerySpec<'a> {
128    /// Get bounds of the widget with the given tag.
129    Bounds(&'a str),
130    /// Check if a tagged widget exists in the tree.
131    Exists(&'a str),
132    /// Count children of the tagged widget.
133    ChildCount(&'a str),
134}
135
136/// Framebuffer dump specification.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub struct DumpSpec {
139    /// Landscape X origin of the dump window.
140    pub x: i32,
141    /// Landscape Y origin of the dump window.
142    pub y: i32,
143    /// Width of the dump window in pixels (clamped 1..=40).
144    pub width: u16,
145    /// Height of the dump window in pixels (clamped 1..=40).
146    pub height: u16,
147    /// Number of frames to capture (clamped 1..=4).
148    pub frames: u8,
149}
150
151// ---------------------------------------------------------------------------
152// Conversions to core types
153// ---------------------------------------------------------------------------
154
155impl KeySpec {
156    /// Convert to the core [`Key`] type.
157    pub fn to_key(self) -> Key {
158        match self {
159            Self::Escape => Key::Escape,
160            Self::Enter => Key::Enter,
161            Self::Space => Key::Space,
162            Self::ArrowUp => Key::ArrowUp,
163            Self::ArrowDown => Key::ArrowDown,
164            Self::ArrowLeft => Key::ArrowLeft,
165            Self::ArrowRight => Key::ArrowRight,
166            Self::Function(n) => Key::Function(n),
167            Self::Character(c) => Key::Character(c),
168            Self::Other(v) => Key::Other(v),
169        }
170    }
171}
172
173impl EventSpec {
174    /// Convert to a core [`Event`].
175    pub fn to_event(self) -> Event {
176        match self {
177            Self::Tick => Event::Tick,
178            Self::PressRelease { x, y } => Event::PressRelease { x, y },
179            Self::PressDown { x, y } => Event::PressDown { x, y },
180            Self::PointerDown { x, y } => Event::PointerDown { x, y },
181            Self::PointerUp { x, y } => Event::PointerUp { x, y },
182            Self::PointerMove { x, y } => Event::PointerMove { x, y },
183            Self::DoubleTap { x, y } => Event::DoubleTap { x, y },
184            Self::KeyDown { key } => Event::KeyDown { key: key.to_key() },
185            Self::KeyUp { key } => Event::KeyUp { key: key.to_key() },
186            Self::Touch { count, points } => {
187                let mut core_points = [TouchPoint::default(); MAX_TOUCH_POINTS];
188                for i in 0..MAX_TOUCH_POINTS {
189                    core_points[i] = TouchPoint {
190                        id: points[i].id,
191                        x: points[i].x,
192                        y: points[i].y,
193                        state: points[i].state.to_core(),
194                    };
195                }
196                Event::Touch {
197                    count,
198                    points: core_points,
199                }
200            }
201        }
202    }
203}