1use minicbor::decode::{Decoder, Error};
10use minicbor::{Decode, Encode};
11
12use crate::cbor::{self, Elements};
13
14pub const MAX_NODE_DEPTH: usize = 256;
19
20#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
22#[cbor(map)]
23pub struct Snapshot {
24 #[cbor(n(0), decode_with = "crate::cbor::decode_vec")]
27 pub strings: Vec<String>,
28 #[cbor(n(1), decode_with = "crate::cbor::decode_vec")]
30 pub windows: Vec<Window>,
31 #[n(2)]
33 pub node_count: u32,
34 #[n(3)]
36 pub truncated: Truncation,
37 #[n(4)]
40 pub seq: u64,
41}
42
43#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode)]
45#[cbor(map)]
46pub struct Window {
47 #[n(0)]
49 pub id: u32,
50 #[n(1)]
51 pub kind: WindowType,
52 #[n(2)]
54 pub layer: i32,
55 #[n(3)]
57 pub package: Option<u32>,
58 #[n(4)]
59 pub title: Option<String>,
60 #[n(5)]
61 pub bounds: Bounds,
62 #[n(6)]
63 pub flags: WindowFlags,
64 #[n(7)]
66 pub root: Option<Node>,
67 #[n(8)]
72 pub emits_events: Option<bool>,
73 #[n(9)]
78 pub owner: Option<u32>,
79 #[cbor(n(10), skip_if = "is_false")]
82 pub modal: bool,
83}
84
85#[derive(Clone, Debug, PartialEq, Eq, Encode)]
90#[cbor(map)]
91pub struct Node {
92 #[n(0)]
94 pub id: u32,
95 #[n(1)]
97 pub class: u32,
98 #[n(2)]
100 pub res_pkg: Option<u32>,
101 #[n(3)]
103 pub res_id: Option<String>,
104 #[n(4)]
105 pub text: Option<String>,
106 #[n(5)]
108 pub desc: Option<String>,
109 #[n(6)]
110 pub bounds: Bounds,
111 #[n(7)]
112 pub flags: NodeFlags,
113 #[cbor(n(8), skip_if = "Vec::is_empty")]
115 pub children: Vec<Node>,
116 #[n(9)]
118 pub scroll: Option<Scroll>,
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encode, Decode)]
127#[cbor(map)]
128pub struct Scroll {
129 #[n(0)]
130 pub axis: ScrollAxis,
131 #[n(1)]
132 pub flags: ScrollFlags,
133 #[n(2)]
135 pub range: Option<ScrollRange>,
136 #[n(3)]
139 pub position: Option<ScrollPosition>,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encode, Decode)]
145#[cbor(array)]
146pub struct ScrollRange {
147 #[n(0)]
148 pub unit: ScrollUnit,
149 #[n(1)]
150 pub min: i32,
151 #[n(2)]
152 pub max: i32,
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encode, Decode)]
159#[cbor(array)]
160pub struct ScrollPosition {
161 #[n(0)]
162 pub first: i32,
163 #[n(1)]
164 pub last: i32,
165}
166
167struct OpenNode {
170 at: usize,
171 entries: Elements,
172 children: Option<Elements>,
173 id: Option<u32>,
174 class: Option<u32>,
175 res_pkg: Option<u32>,
176 res_id: Option<String>,
177 text: Option<String>,
178 desc: Option<String>,
179 bounds: Option<Bounds>,
180 flags: Option<NodeFlags>,
181 child_nodes: Vec<Node>,
182 scroll: Option<Scroll>,
183}
184
185impl OpenNode {
186 fn begin(d: &mut Decoder<'_>) -> Result<OpenNode, Error> {
187 Ok(OpenNode {
188 at: d.position(),
189 entries: Elements::begin_map(d)?,
190 children: None,
191 id: None,
192 class: None,
193 res_pkg: None,
194 res_id: None,
195 text: None,
196 desc: None,
197 bounds: None,
198 flags: None,
199 child_nodes: Vec::new(),
200 scroll: None,
201 })
202 }
203
204 fn read<'b, C>(&mut self, key: i64, d: &mut Decoder<'b>, ctx: &mut C) -> Result<(), Error> {
207 match key {
208 0 => self.id = Some(d.u32()?),
209 1 => self.class = Some(d.u32()?),
210 2 => self.res_pkg = Option::<u32>::decode(d, ctx)?,
211 3 => self.res_id = Option::<String>::decode(d, ctx)?,
212 4 => self.text = Option::<String>::decode(d, ctx)?,
213 5 => self.desc = Option::<String>::decode(d, ctx)?,
214 6 => self.bounds = Some(Bounds::decode(d, ctx)?),
215 7 => self.flags = Some(NodeFlags::decode(d, ctx)?),
216 8 => {
217 let children = Elements::begin(d)?;
218 self.child_nodes = Vec::with_capacity(cbor::reserve(children.len(), d));
219 self.children = Some(children);
220 }
221 9 => self.scroll = Option::<Scroll>::decode(d, ctx)?,
222 _ => d.skip()?,
223 }
224 Ok(())
225 }
226
227 fn finish(self) -> Result<Node, Error> {
228 let at = self.at;
229 Ok(Node {
230 id: self.id.ok_or_else(|| cbor::missing(0, "id").at(at))?,
231 class: self.class.ok_or_else(|| cbor::missing(1, "class").at(at))?,
232 res_pkg: self.res_pkg,
233 res_id: self.res_id,
234 text: self.text,
235 desc: self.desc,
236 bounds: self
237 .bounds
238 .ok_or_else(|| cbor::missing(6, "bounds").at(at))?,
239 flags: self.flags.ok_or_else(|| cbor::missing(7, "flags").at(at))?,
240 children: self.child_nodes,
241 scroll: self.scroll,
242 })
243 }
244}
245
246impl<'b, C> Decode<'b, C> for Node {
247 fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Node, Error> {
248 let mut open = vec![OpenNode::begin(d)?];
249 loop {
250 let depth = open.len();
251 let top = open
252 .last_mut()
253 .expect("a node is open until the root finishes");
254 let child_next = match &mut top.children {
255 Some(children) => {
256 let more = children.next(d)?;
257 if !more {
258 top.children = None;
259 }
260 more
261 }
262 None => false,
263 };
264 if child_next {
265 if depth > MAX_NODE_DEPTH {
266 return Err(Error::message(format!(
267 "node tree deeper than {MAX_NODE_DEPTH} levels"
268 ))
269 .at(d.position()));
270 }
271 open.push(OpenNode::begin(d)?);
272 } else if top.entries.next(d)? {
273 let key = d.i64()?;
274 top.read(key, d, ctx)?;
275 } else {
276 let done = open.pop().expect("checked above").finish()?;
277 match open.last_mut() {
278 Some(parent) => parent.child_nodes.push(done),
279 None => return Ok(done),
280 }
281 }
282 }
283 }
284}
285
286fn is_false(value: &bool) -> bool {
287 !*value
288}
289
290#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Encode, Decode)]
292#[cbor(array)]
293pub struct Bounds {
294 #[n(0)]
295 pub left: i32,
296 #[n(1)]
297 pub top: i32,
298 #[n(2)]
299 pub right: i32,
300 #[n(3)]
301 pub bottom: i32,
302}
303
304impl Bounds {
305 pub const fn new(left: i32, top: i32, right: i32, bottom: i32) -> Bounds {
306 Bounds {
307 left,
308 top,
309 right,
310 bottom,
311 }
312 }
313}
314
315wire_enum! {
316 pub struct WindowType(u8);
318 UNKNOWN = 0, "unknown";
320 APPLICATION = 1, "application";
321 INPUT_METHOD = 2, "input_method";
322 SYSTEM = 3, "system";
323 ACCESSIBILITY_OVERLAY = 4, "accessibility_overlay";
324 SPLIT_SCREEN_DIVIDER = 5, "split_screen_divider";
325 MAGNIFICATION_OVERLAY = 6, "magnification_overlay";
326}
327
328wire_flags! {
329 pub struct NodeFlags;
331 CLICKABLE = 0, "clickable";
332 LONG_CLICKABLE = 1, "long_clickable";
333 FOCUSABLE = 2, "focusable";
334 FOCUSED = 3, "focused";
335 ENABLED = 4, "enabled";
336 CHECKABLE = 5, "checkable";
337 CHECKED = 6, "checked";
338 SCROLLABLE = 7, "scrollable";
339 SELECTED = 8, "selected";
340 EDITABLE = 9, "editable";
341 PASSWORD = 10, "password";
342 VISIBLE = 11, "visible";
344 ACCESSIBILITY_FOCUSED = 12, "accessibility_focused";
345 MODAL = 13, "modal";
349 ON_TOP = 14, "on_top";
353}
354
355wire_flags! {
356 pub struct WindowFlags;
358 ACTIVE = 0, "active";
359 FOCUSED = 1, "focused";
360 ACCESSIBILITY_FOCUSED = 2, "accessibility_focused";
361 PICTURE_IN_PICTURE = 3, "picture_in_picture";
362 TRANSFORMED = 4, "transformed";
368}
369
370wire_enum! {
371 pub struct ScrollAxis(u8);
373 UNKNOWN = 0, "unknown";
375 VERTICAL = 1, "vertical";
376 HORIZONTAL = 2, "horizontal";
377}
378
379wire_enum! {
380 pub struct ScrollUnit(u8);
382 ITEMS = 0, "items";
385 PERCENT = 1, "percent";
388}
389
390wire_flags! {
391 pub struct ScrollFlags;
393 BACK = 0, "back";
395 FORWARD = 1, "forward";
397}
398
399wire_flags! {
400 pub struct Truncation;
402 DEPTH = 0, "depth";
404 NODES = 1, "nodes";
406 WINDOWS = 2, "windows";
408}