Skip to main content

rmux_core/
lifecycle.rs

1use rmux_proto::{HookName, PaneTarget, ScopeSelector, SessionName, Target, WindowTarget};
2
3/// A typed server lifecycle event that may dispatch a registered hook.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum LifecycleEvent {
6    /// A client attached to a session.
7    ClientAttached {
8        /// The session associated with the event.
9        session_name: SessionName,
10        /// The best available rmux client identifier.
11        client_name: Option<String>,
12    },
13    /// A client detached from a session.
14    ClientDetached {
15        /// The session associated with the event.
16        session_name: SessionName,
17        /// The best available rmux client identifier.
18        client_name: Option<String>,
19    },
20    /// A client switched to another session.
21    ClientSessionChanged {
22        /// The session associated with the event.
23        session_name: SessionName,
24        /// The best available rmux client identifier.
25        client_name: Option<String>,
26    },
27    /// A client changed its terminal size.
28    ClientResized {
29        /// The session associated with the event.
30        session_name: SessionName,
31        /// The best available rmux client identifier.
32        client_name: Option<String>,
33    },
34    /// A session was created.
35    SessionCreated {
36        /// The session associated with the event.
37        session_name: SessionName,
38    },
39    /// A session was closed.
40    SessionClosed {
41        /// The session associated with the event.
42        session_name: SessionName,
43        /// The removed session ID captured at notification time, when known.
44        session_id: Option<u32>,
45    },
46    /// A session was renamed.
47    SessionRenamed {
48        /// The session associated with the event.
49        session_name: SessionName,
50    },
51    /// A session's active window changed.
52    SessionWindowChanged {
53        /// The session associated with the event.
54        session_name: SessionName,
55    },
56    /// A window was linked into a session.
57    WindowLinked {
58        /// The session associated with the event.
59        session_name: SessionName,
60        /// The linked window, when it is known at the call site.
61        target: Option<WindowTarget>,
62    },
63    /// A window was unlinked from a session.
64    WindowUnlinked {
65        /// The session associated with the event.
66        session_name: SessionName,
67        /// The unlinked window, when it is known at the call site.
68        target: Option<WindowTarget>,
69        /// The removed window ID captured at notification time, when known.
70        window_id: Option<u32>,
71        /// The removed window name captured at notification time, when known.
72        window_name: Option<String>,
73    },
74    /// A window was renamed.
75    WindowRenamed {
76        /// The targeted window.
77        target: WindowTarget,
78    },
79    /// A window layout changed.
80    WindowLayoutChanged {
81        /// The targeted window.
82        target: WindowTarget,
83    },
84    /// A window size changed.
85    WindowResized {
86        /// The targeted window.
87        target: WindowTarget,
88    },
89    /// A window's active pane changed.
90    WindowPaneChanged {
91        /// The targeted window.
92        target: WindowTarget,
93    },
94    /// A bell alert fired for a window.
95    AlertBell {
96        /// The targeted window.
97        target: WindowTarget,
98    },
99    /// An activity alert fired for a window.
100    AlertActivity {
101        /// The targeted window.
102        target: WindowTarget,
103    },
104    /// A silence alert fired for a window.
105    AlertSilence {
106        /// The targeted window.
107        target: WindowTarget,
108    },
109    /// A pane exited or was removed from service.
110    PaneExited {
111        /// The targeted pane.
112        target: PaneTarget,
113        /// The removed pane ID captured at notification time, when known.
114        pane_id: Option<u32>,
115        /// The removed window ID captured at notification time, when known.
116        window_id: Option<u32>,
117        /// The removed window name captured at notification time, when known.
118        window_name: Option<String>,
119    },
120    /// A pane died and was kept by `remain-on-exit`.
121    PaneDied {
122        /// The targeted pane.
123        target: PaneTarget,
124        /// The dead pane ID captured at notification time, when known.
125        pane_id: Option<u32>,
126        /// The dead pane's window ID captured at notification time, when known.
127        window_id: Option<u32>,
128        /// The dead pane's window name captured at notification time, when known.
129        window_name: Option<String>,
130    },
131    /// A pane mode or display overlay state changed.
132    PaneModeChanged {
133        /// The targeted pane.
134        target: PaneTarget,
135    },
136    /// A pane gained focus.
137    PaneFocusIn {
138        /// The targeted pane.
139        target: PaneTarget,
140    },
141    /// A pane lost focus.
142    PaneFocusOut {
143        /// The targeted pane.
144        target: PaneTarget,
145    },
146    /// A pane title changed.
147    PaneTitleChanged {
148        /// The targeted pane.
149        target: PaneTarget,
150    },
151    /// A paste buffer was created or replaced.
152    PasteBufferChanged {
153        /// The affected paste buffer name.
154        buffer_name: String,
155    },
156    /// A paste buffer was deleted or evicted.
157    PasteBufferDeleted {
158        /// The affected paste buffer name.
159        buffer_name: String,
160    },
161    /// A `select-window`-family command completed successfully.
162    AfterSelectWindow {
163        /// The selected window.
164        target: WindowTarget,
165    },
166    /// A `select-pane`-family command completed successfully.
167    AfterSelectPane {
168        /// The selected pane.
169        target: PaneTarget,
170    },
171    /// A `send-keys` command completed successfully.
172    AfterSendKeys {
173        /// The targeted pane.
174        target: PaneTarget,
175    },
176    /// A `set-option` command completed successfully.
177    AfterSetOption {
178        /// The session associated with the option scope, when one exists.
179        session_name: Option<SessionName>,
180    },
181}
182
183impl LifecycleEvent {
184    /// Returns the hook name corresponding to this lifecycle event.
185    #[must_use]
186    pub const fn hook_name(&self) -> HookName {
187        match self {
188            Self::ClientAttached { .. } => HookName::ClientAttached,
189            Self::ClientDetached { .. } => HookName::ClientDetached,
190            Self::ClientSessionChanged { .. } => HookName::ClientSessionChanged,
191            Self::ClientResized { .. } => HookName::ClientResized,
192            Self::SessionCreated { .. } => HookName::SessionCreated,
193            Self::SessionClosed { .. } => HookName::SessionClosed,
194            Self::SessionRenamed { .. } => HookName::SessionRenamed,
195            Self::SessionWindowChanged { .. } => HookName::SessionWindowChanged,
196            Self::WindowLinked { .. } => HookName::WindowLinked,
197            Self::WindowUnlinked { .. } => HookName::WindowUnlinked,
198            Self::WindowRenamed { .. } => HookName::WindowRenamed,
199            Self::WindowLayoutChanged { .. } => HookName::WindowLayoutChanged,
200            Self::WindowResized { .. } => HookName::WindowResized,
201            Self::WindowPaneChanged { .. } => HookName::WindowPaneChanged,
202            Self::AlertBell { .. } => HookName::AlertBell,
203            Self::AlertActivity { .. } => HookName::AlertActivity,
204            Self::AlertSilence { .. } => HookName::AlertSilence,
205            Self::PaneExited { .. } => HookName::PaneExited,
206            Self::PaneDied { .. } => HookName::PaneDied,
207            Self::PaneModeChanged { .. } => HookName::PaneModeChanged,
208            Self::PaneFocusIn { .. } => HookName::PaneFocusIn,
209            Self::PaneFocusOut { .. } => HookName::PaneFocusOut,
210            Self::PaneTitleChanged { .. } => HookName::PaneTitleChanged,
211            Self::PasteBufferChanged { .. } => HookName::PasteBufferChanged,
212            Self::PasteBufferDeleted { .. } => HookName::PasteBufferDeleted,
213            Self::AfterSelectWindow { .. } => HookName::AfterSelectWindow,
214            Self::AfterSelectPane { .. } => HookName::AfterSelectPane,
215            Self::AfterSendKeys { .. } => HookName::AfterSendKeys,
216            Self::AfterSetOption { .. } => HookName::AfterSetOption,
217        }
218    }
219
220    /// Returns the event scope used for hook resolution.
221    #[must_use]
222    pub fn scope(&self) -> ScopeSelector {
223        match self {
224            Self::ClientAttached { session_name, .. }
225            | Self::ClientDetached { session_name, .. }
226            | Self::ClientSessionChanged { session_name, .. }
227            | Self::ClientResized { session_name, .. }
228            | Self::SessionCreated { session_name }
229            | Self::SessionClosed { session_name, .. }
230            | Self::SessionRenamed { session_name }
231            | Self::SessionWindowChanged { session_name }
232            | Self::WindowLinked { session_name, .. }
233            | Self::WindowUnlinked { session_name, .. } => {
234                ScopeSelector::Session(session_name.clone())
235            }
236            Self::WindowRenamed { target }
237            | Self::WindowLayoutChanged { target }
238            | Self::WindowResized { target }
239            | Self::WindowPaneChanged { target }
240            | Self::AlertBell { target }
241            | Self::AlertActivity { target }
242            | Self::AlertSilence { target }
243            | Self::AfterSelectWindow { target } => ScopeSelector::Window(target.clone()),
244            Self::PaneExited { target, .. }
245            | Self::PaneDied { target, .. }
246            | Self::PaneModeChanged { target }
247            | Self::PaneFocusIn { target }
248            | Self::PaneFocusOut { target }
249            | Self::PaneTitleChanged { target }
250            | Self::AfterSelectPane { target }
251            | Self::AfterSendKeys { target } => ScopeSelector::Pane(target.clone()),
252            Self::PasteBufferChanged { .. } | Self::PasteBufferDeleted { .. } => {
253                ScopeSelector::Global
254            }
255            Self::AfterSetOption {
256                session_name: Some(session_name),
257            } => ScopeSelector::Session(session_name.clone()),
258            Self::AfterSetOption { session_name: None } => ScopeSelector::Global,
259        }
260    }
261
262    /// Returns the session associated with this lifecycle event, when one exists.
263    #[must_use]
264    pub fn session_name(&self) -> Option<&SessionName> {
265        match self {
266            Self::ClientAttached { session_name, .. }
267            | Self::ClientDetached { session_name, .. }
268            | Self::ClientSessionChanged { session_name, .. }
269            | Self::ClientResized { session_name, .. }
270            | Self::SessionCreated { session_name }
271            | Self::SessionClosed { session_name, .. }
272            | Self::SessionRenamed { session_name }
273            | Self::SessionWindowChanged { session_name }
274            | Self::WindowLinked { session_name, .. }
275            | Self::WindowUnlinked { session_name, .. } => Some(session_name),
276            Self::WindowRenamed { target }
277            | Self::WindowLayoutChanged { target }
278            | Self::WindowResized { target }
279            | Self::WindowPaneChanged { target }
280            | Self::AlertBell { target }
281            | Self::AlertActivity { target }
282            | Self::AlertSilence { target }
283            | Self::AfterSelectWindow { target } => Some(target.session_name()),
284            Self::PaneExited { target, .. }
285            | Self::PaneDied { target, .. }
286            | Self::PaneModeChanged { target }
287            | Self::PaneFocusIn { target }
288            | Self::PaneFocusOut { target }
289            | Self::PaneTitleChanged { target }
290            | Self::AfterSelectPane { target }
291            | Self::AfterSendKeys { target } => Some(target.session_name()),
292            Self::AfterSetOption { session_name } => session_name.as_ref(),
293            Self::PasteBufferChanged { .. } | Self::PasteBufferDeleted { .. } => None,
294        }
295    }
296
297    /// Returns the event client identifier when one is available.
298    #[must_use]
299    pub fn client_name(&self) -> Option<&str> {
300        match self {
301            Self::ClientAttached { client_name, .. }
302            | Self::ClientDetached { client_name, .. }
303            | Self::ClientSessionChanged { client_name, .. }
304            | Self::ClientResized { client_name, .. } => client_name.as_deref(),
305            _ => None,
306        }
307    }
308
309    /// Returns the event window target when one is available.
310    #[must_use]
311    pub fn window_target(&self) -> Option<WindowTarget> {
312        match self {
313            Self::WindowLinked { target, .. } | Self::WindowUnlinked { target, .. } => {
314                target.clone()
315            }
316            Self::WindowRenamed { target }
317            | Self::WindowLayoutChanged { target }
318            | Self::WindowResized { target }
319            | Self::WindowPaneChanged { target }
320            | Self::AlertBell { target }
321            | Self::AlertActivity { target }
322            | Self::AlertSilence { target }
323            | Self::AfterSelectWindow { target } => Some(target.clone()),
324            Self::PaneExited { target, .. }
325            | Self::PaneDied { target, .. }
326            | Self::PaneModeChanged { target }
327            | Self::PaneFocusIn { target }
328            | Self::PaneFocusOut { target }
329            | Self::PaneTitleChanged { target }
330            | Self::AfterSelectPane { target }
331            | Self::AfterSendKeys { target } => Some(WindowTarget::with_window(
332                target.session_name().clone(),
333                target.window_index(),
334            )),
335            _ => None,
336        }
337    }
338
339    /// Returns the event pane target when one is available.
340    #[must_use]
341    pub fn pane_target(&self) -> Option<&PaneTarget> {
342        match self {
343            Self::PaneExited { target, .. }
344            | Self::PaneDied { target, .. }
345            | Self::PaneModeChanged { target }
346            | Self::PaneFocusIn { target }
347            | Self::PaneFocusOut { target }
348            | Self::PaneTitleChanged { target }
349            | Self::AfterSelectPane { target }
350            | Self::AfterSendKeys { target } => Some(target),
351            _ => None,
352        }
353    }
354
355    /// Returns the event session ID captured at notification time, when one exists.
356    #[must_use]
357    pub const fn session_id(&self) -> Option<u32> {
358        match self {
359            Self::SessionClosed { session_id, .. } => *session_id,
360            _ => None,
361        }
362    }
363
364    /// Returns the event window ID captured at notification time, when one exists.
365    #[must_use]
366    pub const fn window_id(&self) -> Option<u32> {
367        match self {
368            Self::WindowUnlinked { window_id, .. }
369            | Self::PaneExited { window_id, .. }
370            | Self::PaneDied { window_id, .. } => *window_id,
371            _ => None,
372        }
373    }
374
375    /// Returns the event window name captured at notification time, when one exists.
376    #[must_use]
377    pub fn window_name_snapshot(&self) -> Option<&str> {
378        match self {
379            Self::WindowUnlinked { window_name, .. }
380            | Self::PaneExited { window_name, .. }
381            | Self::PaneDied { window_name, .. } => window_name.as_deref(),
382            _ => None,
383        }
384    }
385
386    /// Returns the event pane ID captured at notification time, when one exists.
387    #[must_use]
388    pub const fn pane_id(&self) -> Option<u32> {
389        match self {
390            Self::PaneExited { pane_id, .. } | Self::PaneDied { pane_id, .. } => *pane_id,
391            _ => None,
392        }
393    }
394
395    /// Returns the paste buffer name when one is available.
396    #[must_use]
397    pub fn buffer_name(&self) -> Option<&str> {
398        match self {
399            Self::PasteBufferChanged { buffer_name } | Self::PasteBufferDeleted { buffer_name } => {
400                Some(buffer_name)
401            }
402            _ => None,
403        }
404    }
405
406    /// Returns the best available current target for hook execution.
407    #[must_use]
408    pub fn current_target(&self) -> Option<Target> {
409        match self {
410            Self::WindowLinked {
411                session_name,
412                target: Some(target),
413            }
414            | Self::WindowUnlinked {
415                session_name,
416                target: Some(target),
417                ..
418            } => Some(Target::Window(WindowTarget::with_window(
419                session_name.clone(),
420                target.window_index(),
421            ))),
422            Self::WindowLinked { session_name, .. }
423            | Self::WindowUnlinked { session_name, .. }
424            | Self::ClientAttached { session_name, .. }
425            | Self::ClientDetached { session_name, .. }
426            | Self::ClientSessionChanged { session_name, .. }
427            | Self::ClientResized { session_name, .. }
428            | Self::SessionCreated { session_name }
429            | Self::SessionClosed { session_name, .. }
430            | Self::SessionRenamed { session_name }
431            | Self::SessionWindowChanged { session_name } => {
432                Some(Target::Session(session_name.clone()))
433            }
434            Self::WindowRenamed { target }
435            | Self::WindowLayoutChanged { target }
436            | Self::WindowResized { target }
437            | Self::WindowPaneChanged { target }
438            | Self::AlertBell { target }
439            | Self::AlertActivity { target }
440            | Self::AlertSilence { target }
441            | Self::AfterSelectWindow { target } => Some(Target::Window(target.clone())),
442            Self::PaneExited { target, .. }
443            | Self::PaneDied { target, .. }
444            | Self::PaneModeChanged { target }
445            | Self::PaneFocusIn { target }
446            | Self::PaneFocusOut { target }
447            | Self::PaneTitleChanged { target }
448            | Self::AfterSelectPane { target }
449            | Self::AfterSendKeys { target } => Some(Target::Pane(target.clone())),
450            Self::AfterSetOption {
451                session_name: Some(session_name),
452            } => Some(Target::Session(session_name.clone())),
453            Self::AfterSetOption { session_name: None }
454            | Self::PasteBufferChanged { .. }
455            | Self::PasteBufferDeleted { .. } => None,
456        }
457    }
458}