niri_ipc/
state.rs

1//! Helpers for keeping track of the event stream state.
2//!
3//! 1. Create an [`EventStreamState`] using `Default::default()`, or any individual state part if
4//!    you only care about part of the state.
5//! 2. Connect to the niri socket and request an event stream.
6//! 3. Pass every [`Event`] to [`EventStreamStatePart::apply`] on your state.
7//! 4. Read the fields of the state as needed.
8
9use std::collections::hash_map::Entry;
10use std::collections::HashMap;
11
12use crate::{Cast, Event, KeyboardLayouts, Window, Workspace};
13
14/// Part of the state communicated via the event stream.
15pub trait EventStreamStatePart {
16    /// Returns a sequence of events that replicates this state from default initialization.
17    fn replicate(&self) -> Vec<Event>;
18
19    /// Applies the event to this state.
20    ///
21    /// Returns `None` after applying the event, and `Some(event)` if the event is ignored by this
22    /// part of the state.
23    fn apply(&mut self, event: Event) -> Option<Event>;
24}
25
26/// The full state communicated over the event stream.
27///
28/// Different parts of the state are not guaranteed to be consistent across every single event
29/// sent by niri. For example, you may receive the first [`Event::WindowOpenedOrChanged`] for a
30/// just-opened window *after* an [`Event::WorkspaceActiveWindowChanged`] for that window. Between
31/// these two events, the workspace active window id refers to a window that does not yet exist in
32/// the windows state part.
33#[derive(Debug, Default)]
34pub struct EventStreamState {
35    /// State of workspaces.
36    pub workspaces: WorkspacesState,
37
38    /// State of workspaces.
39    pub windows: WindowsState,
40
41    /// State of the keyboard layouts.
42    pub keyboard_layouts: KeyboardLayoutsState,
43
44    /// State of the overview.
45    pub overview: OverviewState,
46
47    /// State of the config.
48    pub config: ConfigState,
49
50    /// State of screencasts.
51    pub casts: CastsState,
52}
53
54/// The workspaces state communicated over the event stream.
55#[derive(Debug, Default)]
56pub struct WorkspacesState {
57    /// Map from a workspace id to the workspace.
58    pub workspaces: HashMap<u64, Workspace>,
59}
60
61/// The windows state communicated over the event stream.
62#[derive(Debug, Default)]
63pub struct WindowsState {
64    /// Map from a window id to the window.
65    pub windows: HashMap<u64, Window>,
66}
67
68/// The keyboard layout state communicated over the event stream.
69#[derive(Debug, Default)]
70pub struct KeyboardLayoutsState {
71    /// Configured keyboard layouts.
72    pub keyboard_layouts: Option<KeyboardLayouts>,
73}
74
75/// The overview state communicated over the event stream.
76#[derive(Debug, Default)]
77pub struct OverviewState {
78    /// Whether the overview is currently open.
79    pub is_open: bool,
80}
81
82/// The config state communicated over the event stream.
83#[derive(Debug, Default)]
84pub struct ConfigState {
85    /// Whether the last config load attempt had failed.
86    pub failed: bool,
87}
88
89/// The casts state communicated over the event stream.
90#[derive(Debug, Default)]
91pub struct CastsState {
92    /// Map from a stream id to the screencast.
93    pub casts: HashMap<u64, Cast>,
94}
95
96impl EventStreamStatePart for EventStreamState {
97    fn replicate(&self) -> Vec<Event> {
98        let mut events = Vec::new();
99        events.extend(self.workspaces.replicate());
100        events.extend(self.windows.replicate());
101        events.extend(self.keyboard_layouts.replicate());
102        events.extend(self.overview.replicate());
103        events.extend(self.config.replicate());
104        events.extend(self.casts.replicate());
105        events
106    }
107
108    fn apply(&mut self, event: Event) -> Option<Event> {
109        let event = self.workspaces.apply(event)?;
110        let event = self.windows.apply(event)?;
111        let event = self.keyboard_layouts.apply(event)?;
112        let event = self.overview.apply(event)?;
113        let event = self.config.apply(event)?;
114        let event = self.casts.apply(event)?;
115        Some(event)
116    }
117}
118
119impl EventStreamStatePart for WorkspacesState {
120    fn replicate(&self) -> Vec<Event> {
121        let workspaces = self.workspaces.values().cloned().collect();
122        vec![Event::WorkspacesChanged { workspaces }]
123    }
124
125    fn apply(&mut self, event: Event) -> Option<Event> {
126        match event {
127            Event::WorkspacesChanged { workspaces } => {
128                self.workspaces = workspaces.into_iter().map(|ws| (ws.id, ws)).collect();
129            }
130            Event::WorkspaceUrgencyChanged { id, urgent } => {
131                for ws in self.workspaces.values_mut() {
132                    if ws.id == id {
133                        ws.is_urgent = urgent;
134                    }
135                }
136            }
137            Event::WorkspaceActivated { id, focused } => {
138                let ws = self.workspaces.get(&id);
139                let ws = ws.expect("activated workspace was missing from the map");
140                let output = ws.output.clone();
141
142                for ws in self.workspaces.values_mut() {
143                    let got_activated = ws.id == id;
144                    if ws.output == output {
145                        ws.is_active = got_activated;
146                    }
147
148                    if focused {
149                        ws.is_focused = got_activated;
150                    }
151                }
152            }
153            Event::WorkspaceActiveWindowChanged {
154                workspace_id,
155                active_window_id,
156            } => {
157                let ws = self.workspaces.get_mut(&workspace_id);
158                let ws = ws.expect("changed workspace was missing from the map");
159                ws.active_window_id = active_window_id;
160            }
161            event => return Some(event),
162        }
163        None
164    }
165}
166
167impl EventStreamStatePart for WindowsState {
168    fn replicate(&self) -> Vec<Event> {
169        let windows = self.windows.values().cloned().collect();
170        vec![Event::WindowsChanged { windows }]
171    }
172
173    fn apply(&mut self, event: Event) -> Option<Event> {
174        match event {
175            Event::WindowsChanged { windows } => {
176                self.windows = windows.into_iter().map(|win| (win.id, win)).collect();
177            }
178            Event::WindowOpenedOrChanged { window } => {
179                let (id, is_focused) = match self.windows.entry(window.id) {
180                    Entry::Occupied(mut entry) => {
181                        let entry = entry.get_mut();
182                        *entry = window;
183                        (entry.id, entry.is_focused)
184                    }
185                    Entry::Vacant(entry) => {
186                        let entry = entry.insert(window);
187                        (entry.id, entry.is_focused)
188                    }
189                };
190
191                if is_focused {
192                    for win in self.windows.values_mut() {
193                        if win.id != id {
194                            win.is_focused = false;
195                        }
196                    }
197                }
198            }
199            Event::WindowClosed { id } => {
200                let win = self.windows.remove(&id);
201                win.expect("closed window was missing from the map");
202            }
203            Event::WindowFocusChanged { id } => {
204                for win in self.windows.values_mut() {
205                    win.is_focused = Some(win.id) == id;
206                }
207            }
208            Event::WindowFocusTimestampChanged {
209                id,
210                focus_timestamp,
211            } => {
212                for win in self.windows.values_mut() {
213                    if win.id == id {
214                        win.focus_timestamp = focus_timestamp;
215                        break;
216                    }
217                }
218            }
219            Event::WindowUrgencyChanged { id, urgent } => {
220                for win in self.windows.values_mut() {
221                    if win.id == id {
222                        win.is_urgent = urgent;
223                        break;
224                    }
225                }
226            }
227            Event::WindowLayoutsChanged { changes } => {
228                for (id, update) in changes {
229                    let win = self.windows.get_mut(&id);
230                    let win = win.expect("changed window was missing from the map");
231                    win.layout = update;
232                }
233            }
234            event => return Some(event),
235        }
236        None
237    }
238}
239
240impl EventStreamStatePart for KeyboardLayoutsState {
241    fn replicate(&self) -> Vec<Event> {
242        if let Some(keyboard_layouts) = self.keyboard_layouts.clone() {
243            vec![Event::KeyboardLayoutsChanged { keyboard_layouts }]
244        } else {
245            vec![]
246        }
247    }
248
249    fn apply(&mut self, event: Event) -> Option<Event> {
250        match event {
251            Event::KeyboardLayoutsChanged { keyboard_layouts } => {
252                self.keyboard_layouts = Some(keyboard_layouts);
253            }
254            Event::KeyboardLayoutSwitched { idx } => {
255                let kb = self.keyboard_layouts.as_mut();
256                let kb = kb.expect("keyboard layouts must be set before a layout can be switched");
257                kb.current_idx = idx;
258            }
259            event => return Some(event),
260        }
261        None
262    }
263}
264
265impl EventStreamStatePart for OverviewState {
266    fn replicate(&self) -> Vec<Event> {
267        vec![Event::OverviewOpenedOrClosed {
268            is_open: self.is_open,
269        }]
270    }
271
272    fn apply(&mut self, event: Event) -> Option<Event> {
273        match event {
274            Event::OverviewOpenedOrClosed { is_open } => {
275                self.is_open = is_open;
276            }
277            event => return Some(event),
278        }
279        None
280    }
281}
282
283impl EventStreamStatePart for ConfigState {
284    fn replicate(&self) -> Vec<Event> {
285        vec![Event::ConfigLoaded {
286            failed: self.failed,
287        }]
288    }
289
290    fn apply(&mut self, event: Event) -> Option<Event> {
291        match event {
292            Event::ConfigLoaded { failed } => {
293                self.failed = failed;
294            }
295            event => return Some(event),
296        }
297        None
298    }
299}
300
301impl EventStreamStatePart for CastsState {
302    fn replicate(&self) -> Vec<Event> {
303        let casts = self.casts.values().cloned().collect();
304        vec![Event::CastsChanged { casts }]
305    }
306
307    fn apply(&mut self, event: Event) -> Option<Event> {
308        match event {
309            Event::CastsChanged { casts } => {
310                self.casts = casts.into_iter().map(|c| (c.stream_id, c)).collect();
311            }
312            Event::CastStartedOrChanged { cast } => {
313                self.casts.insert(cast.stream_id, cast);
314            }
315            Event::CastStopped { stream_id } => {
316                let cast = self.casts.remove(&stream_id);
317                cast.expect("stopped cast was missing from the map");
318            }
319            event => return Some(event),
320        }
321        None
322    }
323}