Skip to main content

protocol/
event.rs

1//! Events: unsolicited messages on per-stream sequences (spec: Event channel
2//! with sequence numbers).
3//!
4//! On the wire an event is a positional array `[4, stream, seq, time_us,
5//! ...]` whose remaining elements depend on the stream.
6
7use minicbor::decode::{self, Decoder};
8use minicbor::encode::{self, Encoder, Write};
9use minicbor::{Decode, Encode};
10
11use crate::cbor::{self, Elements};
12use crate::message::{Rotation, kind};
13
14/// One event.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct Event {
17    /// Per stream and per connection: 1 for the first event, then one more
18    /// per event on a lossless stream; increasing, possibly with gaps, on a
19    /// lossy one.
20    pub seq: u64,
21    /// Monotonic device time in microseconds.
22    pub time_us: u64,
23    pub payload: EventPayload,
24}
25
26impl Event {
27    pub fn stream(&self) -> Stream {
28        self.payload.stream()
29    }
30}
31
32/// What an event says; the variant determines the stream.
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub enum EventPayload {
35    Tree(TreeChange),
36    Window(WindowChange),
37    Frame(FrameAvailable),
38    Lifecycle(Lifecycle),
39    /// An event on a stream this version does not know; its stream-specific
40    /// elements were skipped.
41    Unknown(Stream),
42}
43
44impl EventPayload {
45    pub fn stream(&self) -> Stream {
46        match self {
47            EventPayload::Tree(_) => Stream::TREE,
48            EventPayload::Window(_) => Stream::WINDOW,
49            EventPayload::Frame(_) => Stream::FRAME,
50            EventPayload::Lifecycle(_) => Stream::LIFECYCLE,
51            EventPayload::Unknown(stream) => *stream,
52        }
53    }
54}
55
56wire_enum! {
57    /// An event stream.
58    pub struct Stream(u8);
59    /// Tree changes: `[change, window, path]`. Lossless.
60    TREE = 0, "tree";
61    /// Window changes: `[change, window]`. Lossless.
62    WINDOW = 1, "window";
63    /// Frame availability: `[width, height, rotation]`. Lossy.
64    FRAME = 2, "frame";
65    /// Agent lifecycle: `[change, message]`. Lossless.
66    LIFECYCLE = 3, "lifecycle";
67}
68
69impl Stream {
70    /// Whether the stream may skip sequence numbers without losing state, so
71    /// a gap counts drops instead of requiring a resync. Only frame
72    /// availability is lossy; unknown streams are treated as lossless.
73    pub fn is_lossy(self) -> bool {
74        self == Stream::FRAME
75    }
76}
77
78/// A change inside one window's tree.
79#[derive(Clone, Debug, PartialEq, Eq)]
80pub struct TreeChange {
81    pub change: TreeChangeKind,
82    pub window: u32,
83    /// Child indices from the window root to the node the change is about.
84    pub path: Vec<u32>,
85}
86
87wire_enum! {
88    /// What changed in a tree.
89    pub struct TreeChangeKind(u8);
90    /// The subtree's structure or content.
91    CONTENT = 0, "content";
92    TEXT = 1, "text";
93    SCROLL = 2, "scroll";
94    FOCUS = 3, "focus";
95    SELECTION = 4, "selection";
96}
97
98/// A change to the set or state of windows.
99#[derive(Clone, Debug, PartialEq, Eq)]
100pub struct WindowChange {
101    pub change: WindowChangeKind,
102    pub window: u32,
103}
104
105wire_enum! {
106    /// What changed about a window.
107    pub struct WindowChangeKind(u8);
108    /// Its state, such as a new activity or dialog.
109    STATE = 0, "state";
110    ADDED = 1, "added";
111    REMOVED = 2, "removed";
112    /// Which window is active or focused.
113    FOCUS = 3, "focus";
114    BOUNDS = 4, "bounds";
115}
116
117/// A new frame is available.
118#[derive(Clone, Debug, PartialEq, Eq)]
119pub struct FrameAvailable {
120    pub width: u32,
121    pub height: u32,
122    pub rotation: Rotation,
123}
124
125/// An agent lifecycle change.
126#[derive(Clone, Debug, PartialEq, Eq)]
127pub struct Lifecycle {
128    pub change: LifecycleKind,
129    pub message: String,
130}
131
132wire_enum! {
133    /// What happened to the agent.
134    pub struct LifecycleKind(u8);
135    /// Attached and serving requests.
136    READY = 0, "ready";
137    /// Another process took the UI automation connection.
138    OWNERSHIP_LOST = 1, "ownership_lost";
139    /// About to exit.
140    SHUTTING_DOWN = 2, "shutting_down";
141    /// Still running with reduced ability; the message says what.
142    WARNING = 3, "warning";
143}
144
145impl<C> Encode<C> for Event {
146    fn encode<W: Write>(
147        &self,
148        e: &mut Encoder<W>,
149        ctx: &mut C,
150    ) -> Result<(), encode::Error<W::Error>> {
151        let extra: u64 = match &self.payload {
152            EventPayload::Tree(_) | EventPayload::Frame(_) => 3,
153            EventPayload::Window(_) | EventPayload::Lifecycle(_) => 2,
154            EventPayload::Unknown(_) => 0,
155        };
156        e.array(4 + extra)?
157            .u8(kind::EVENT)?
158            .u8(self.stream().0)?
159            .u64(self.seq)?
160            .u64(self.time_us)?;
161        match &self.payload {
162            EventPayload::Tree(t) => {
163                e.u8(t.change.0)?.u32(t.window)?.encode_with(&t.path, ctx)?;
164            }
165            EventPayload::Window(w) => {
166                e.u8(w.change.0)?.u32(w.window)?;
167            }
168            EventPayload::Frame(f) => {
169                e.u32(f.width)?.u32(f.height)?.u8(f.rotation.0)?;
170            }
171            EventPayload::Lifecycle(l) => {
172                e.u8(l.change.0)?.str(&l.message)?;
173            }
174            EventPayload::Unknown(_) => {}
175        }
176        Ok(())
177    }
178}
179
180impl<'b, C> Decode<'b, C> for Event {
181    fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Event, decode::Error> {
182        let at = d.position();
183        let mut items = Elements::begin(d)?;
184        let k = items.require(d, 0, "kind", |d| d.u8())?;
185        cbor::expect_kind(Some(k), kind::EVENT, at)?;
186        let stream = Stream(items.require(d, 1, "stream", |d| d.u8())?);
187        let seq = items.require(d, 2, "seq", |d| d.u64())?;
188        let time_us = items.require(d, 3, "time_us", |d| d.u64())?;
189        let payload = match stream {
190            Stream::TREE => EventPayload::Tree(TreeChange {
191                change: TreeChangeKind(items.require(d, 4, "change", |d| d.u8())?),
192                window: items.require(d, 5, "window", |d| d.u32())?,
193                path: items.require(d, 6, "path", |d| cbor::decode_vec(d, &mut *ctx))?,
194            }),
195            Stream::WINDOW => EventPayload::Window(WindowChange {
196                change: WindowChangeKind(items.require(d, 4, "change", |d| d.u8())?),
197                window: items.require(d, 5, "window", |d| d.u32())?,
198            }),
199            Stream::FRAME => EventPayload::Frame(FrameAvailable {
200                width: items.require(d, 4, "width", |d| d.u32())?,
201                height: items.require(d, 5, "height", |d| d.u32())?,
202                rotation: Rotation(items.require(d, 6, "rotation", |d| d.u8())?),
203            }),
204            Stream::LIFECYCLE => EventPayload::Lifecycle(Lifecycle {
205                change: LifecycleKind(items.require(d, 4, "change", |d| d.u8())?),
206                message: items.require(d, 5, "message", |d| d.str().map(str::to_owned))?,
207            }),
208            other => EventPayload::Unknown(other),
209        };
210        items.skip_rest(d)?;
211        Ok(Event {
212            seq,
213            time_us,
214            payload,
215        })
216    }
217}