hyprland/event_listener/
shared.rs

1use crate::shared::*;
2use regex::{Error as RegexError, Regex};
3use std::fmt::Debug;
4use std::io;
5use std::pin::Pin;
6
7/// This trait provides shared behaviour for listener types
8#[async_trait]
9pub(crate) trait Listener: HasExecutor {
10    /// This method starts the event listener
11    fn start_listener() -> crate::Result<()>;
12}
13
14/// This trait provides shared behaviour for listener types
15#[async_trait]
16pub(crate) trait AsyncListener: HasAsyncExecutor {
17    /// This method starts the event listener (async)
18    async fn start_listener_async() -> crate::Result<()>;
19}
20
21#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
22pub(crate) enum ActiveWindowValue<T> {
23    Queued(T), // aka Some(T)
24    None,      // No current window
25    Empty,     // Empty queue
26}
27
28impl<T> ActiveWindowValue<T> {
29    pub fn reset(&mut self) {
30        *self = Self::Empty;
31    }
32    pub fn is_empty(&self) -> bool {
33        matches!(self, Self::Empty)
34    }
35}
36
37#[derive(Clone, Debug)]
38pub(crate) struct ActiveWindowState {
39    pub class: ActiveWindowValue<String>,
40    pub title: ActiveWindowValue<String>,
41    pub addr: ActiveWindowValue<Address>,
42}
43
44pub(crate) trait HasExecutor {
45    fn event_executor(&mut self, event: &Event) -> crate::Result<()>;
46
47    fn event_primer(
48        &mut self,
49        event: &Event,
50        abuf: &mut Vec<ActiveWindowState>,
51    ) -> crate::Result<()>
52    where
53        Self: std::marker::Sized,
54    {
55        if abuf.is_empty() {
56            abuf.push(ActiveWindowState::new());
57        }
58        if let Event::ActiveWindowChangedV1(data) = event {
59            let mut to_remove = vec![];
60            for (index, awin) in abuf.iter_mut().enumerate() {
61                if awin.title.is_empty() && awin.class.is_empty() {
62                    awin.class = data.clone().map(|i| i.0).into();
63                    awin.title = data.clone().map(|i| i.1).into();
64                }
65                if awin.ready() {
66                    awin.execute(self)?;
67                    to_remove.push(index);
68                    break;
69                }
70            }
71            for index in to_remove {
72                abuf.remove(index);
73            }
74        } else if let Event::ActiveWindowChangedV2(data) = event {
75            let mut to_remove = vec![];
76            for (index, awin) in abuf.iter_mut().enumerate() {
77                if awin.addr.is_empty() {
78                    awin.addr = data.clone().into();
79                }
80                if awin.ready() {
81                    awin.execute(self)?;
82                    to_remove.push(index);
83                    break;
84                }
85            }
86            for index in to_remove {
87                abuf.remove(index);
88            }
89        } else {
90            self.event_executor(event)?;
91        }
92        Ok(())
93    }
94}
95
96#[async_trait]
97pub(crate) trait HasAsyncExecutor {
98    async fn event_executor_async(&mut self, event: &Event) -> crate::Result<()>;
99
100    async fn event_primer_async(
101        &mut self,
102        event: &Event,
103        abuf: &mut Vec<ActiveWindowState>,
104    ) -> crate::Result<()>
105    where
106        Self: std::marker::Sized,
107    {
108        if abuf.is_empty() {
109            abuf.push(ActiveWindowState::new());
110        }
111        if let Event::ActiveWindowChangedV1(data) = event {
112            let mut to_remove = vec![];
113            for (index, awin) in abuf.iter_mut().enumerate() {
114                if awin.title.is_empty() && awin.class.is_empty() {
115                    awin.class = data.clone().map(|i| i.0).into();
116                    awin.title = data.clone().map(|i| i.1).into();
117                }
118                if awin.ready() {
119                    awin.execute_async(self).await?;
120                    to_remove.push(index);
121                    break;
122                }
123            }
124            for index in to_remove {
125                abuf.remove(index);
126            }
127        } else if let Event::ActiveWindowChangedV2(data) = event {
128            let mut to_remove = vec![];
129            for (index, awin) in abuf.iter_mut().enumerate() {
130                if awin.addr.is_empty() {
131                    awin.addr = data.clone().into();
132                }
133                if awin.ready() {
134                    awin.execute_async(self).await?;
135                    to_remove.push(index);
136                    break;
137                }
138            }
139            for index in to_remove {
140                abuf.remove(index);
141            }
142        } else {
143            self.event_executor_async(event).await?;
144        }
145        Ok(())
146    }
147}
148
149impl ActiveWindowState {
150    pub fn execute<T: HasExecutor>(&mut self, listener: &mut T) -> crate::Result<()> {
151        use ActiveWindowValue::{None, Queued};
152        let data = (&self.title, &self.class, &self.addr);
153        if let (Queued(ref title), Queued(ref class), Queued(ref addr)) = data {
154            listener.event_executor(&Event::ActiveWindowChangedMerged(Some(WindowEventData {
155                window_class: class.to_string(),
156                window_title: title.to_string(),
157                window_address: addr.clone(),
158            })))?;
159            self.reset();
160        } else if let (None, None, None) = data {
161            listener.event_executor(&Event::ActiveWindowChangedMerged(Option::None))?;
162        }
163        Ok(())
164    }
165    pub async fn execute_async<T: HasAsyncExecutor>(
166        &mut self,
167        listener: &mut T,
168    ) -> crate::Result<()> {
169        use ActiveWindowValue::{None, Queued};
170        let data = (&self.title, &self.class, &self.addr);
171        if let (Queued(ref title), Queued(ref class), Queued(ref addr)) = data {
172            listener
173                .event_executor_async(&Event::ActiveWindowChangedMerged(Some(WindowEventData {
174                    window_class: class.to_string(),
175                    window_title: title.to_string(),
176                    window_address: addr.clone(),
177                })))
178                .await?;
179            self.reset();
180        } else if let (None, None, None) = data {
181            listener
182                .event_executor_async(&Event::ActiveWindowChangedMerged(Option::None))
183                .await?;
184        }
185        Ok(())
186    }
187    pub async fn execute_async_mut(
188        &mut self,
189        listener: &mut super::EventListenerMutable,
190    ) -> crate::Result<()> {
191        use ActiveWindowValue::{None, Queued};
192        let data = (&self.title, &self.class, &self.addr);
193        if let (Queued(ref title), Queued(ref class), Queued(ref addr)) = data {
194            listener
195                .event_executor_async(&Event::ActiveWindowChangedMerged(Some(WindowEventData {
196                    window_class: class.to_string(),
197                    window_title: title.to_string(),
198                    window_address: addr.clone(),
199                })))
200                .await?;
201            self.reset();
202        } else if let (None, None, None) = data {
203            listener
204                .event_executor_async(&Event::ActiveWindowChangedMerged(Option::None))
205                .await?;
206        }
207        Ok(())
208    }
209
210    pub fn ready(&self) -> bool {
211        !self.class.is_empty() && !self.title.is_empty() && !self.addr.is_empty()
212    }
213    pub fn reset(&mut self) {
214        self.class.reset();
215        self.title.reset();
216        self.addr.reset();
217    }
218    pub fn new() -> Self {
219        Self {
220            class: ActiveWindowValue::Empty,
221            title: ActiveWindowValue::Empty,
222            addr: ActiveWindowValue::Empty,
223        }
224    }
225}
226
227impl<T> From<Option<T>> for ActiveWindowValue<T> {
228    fn from(value: Option<T>) -> Self {
229        match value {
230            Some(v) => ActiveWindowValue::Queued(v),
231            None => ActiveWindowValue::None,
232        }
233    }
234}
235
236pub(crate) enum EventTypes<T: ?Sized, U: ?Sized> {
237    MutableState(Box<U>),
238    Regular(Box<T>),
239}
240
241pub(crate) enum AsyncEventTypes<T: ?Sized, U: ?Sized> {
242    #[allow(dead_code)]
243    MutableState(Pin<Box<U>>),
244    Regular(Pin<Box<T>>),
245}
246
247pub(crate) type VoidFuture = std::pin::Pin<Box<dyn futures::Future<Output = ()> + Send>>;
248pub(crate) type VoidFutureMut =
249    std::pin::Pin<Box<dyn futures::Future<Output = ()> + Send + 'static>>;
250
251pub(crate) type Closure<T> = EventTypes<dyn Fn(T), dyn Fn(T, &mut State)>;
252pub(crate) type AsyncClosure<T> = AsyncEventTypes<
253    dyn Sync + Send + Fn(T) -> VoidFuture,
254    dyn Sync + Send + Fn(T, &mut State) -> VoidFutureMut,
255>;
256pub(crate) type Closures<T> = Vec<Closure<T>>;
257pub(crate) type AsyncClosures<T> = Vec<AsyncClosure<T>>;
258
259#[allow(clippy::type_complexity)]
260pub(crate) struct Events {
261    pub(crate) workspace_changed_events: Closures<WorkspaceType>,
262    pub(crate) workspace_added_events: Closures<WorkspaceType>,
263    pub(crate) workspace_destroyed_events: Closures<WorkspaceType>,
264    pub(crate) workspace_moved_events: Closures<MonitorEventData>,
265    pub(crate) workspace_rename_events: Closures<WorkspaceRenameEventData>,
266    pub(crate) active_monitor_changed_events: Closures<MonitorEventData>,
267    pub(crate) active_window_changed_events: Closures<Option<WindowEventData>>,
268    pub(crate) fullscreen_state_changed_events: Closures<bool>,
269    pub(crate) monitor_removed_events: Closures<String>,
270    pub(crate) monitor_added_events: Closures<String>,
271    pub(crate) keyboard_layout_change_events: Closures<LayoutEvent>,
272    pub(crate) sub_map_changed_events: Closures<String>,
273    pub(crate) window_open_events: Closures<WindowOpenEvent>,
274    pub(crate) window_close_events: Closures<Address>,
275    pub(crate) window_moved_events: Closures<WindowMoveEvent>,
276    pub(crate) layer_open_events: Closures<String>,
277    pub(crate) layer_closed_events: Closures<String>,
278    pub(crate) float_state_events: Closures<WindowFloatEventData>,
279    pub(crate) urgent_state_events: Closures<Address>,
280    pub(crate) minimize_events: Closures<MinimizeEventData>,
281    pub(crate) window_title_changed_events: Closures<Address>,
282    pub(crate) screencast_events: Closures<ScreencastEventData>,
283}
284
285#[allow(clippy::type_complexity)]
286pub(crate) struct AsyncEvents {
287    pub(crate) workspace_changed_events: AsyncClosures<WorkspaceType>,
288    pub(crate) workspace_added_events: AsyncClosures<WorkspaceType>,
289    pub(crate) workspace_destroyed_events: AsyncClosures<WorkspaceType>,
290    pub(crate) workspace_moved_events: AsyncClosures<MonitorEventData>,
291    pub(crate) workspace_rename_events: AsyncClosures<WorkspaceRenameEventData>,
292    pub(crate) active_monitor_changed_events: AsyncClosures<MonitorEventData>,
293    pub(crate) active_window_changed_events: AsyncClosures<Option<WindowEventData>>,
294    pub(crate) fullscreen_state_changed_events: AsyncClosures<bool>,
295    pub(crate) monitor_removed_events: AsyncClosures<String>,
296    pub(crate) monitor_added_events: AsyncClosures<String>,
297    pub(crate) keyboard_layout_change_events: AsyncClosures<LayoutEvent>,
298    pub(crate) sub_map_changed_events: AsyncClosures<String>,
299    pub(crate) window_open_events: AsyncClosures<WindowOpenEvent>,
300    pub(crate) window_close_events: AsyncClosures<Address>,
301    pub(crate) window_moved_events: AsyncClosures<WindowMoveEvent>,
302    pub(crate) layer_open_events: AsyncClosures<String>,
303    pub(crate) layer_closed_events: AsyncClosures<String>,
304    pub(crate) float_state_events: AsyncClosures<WindowFloatEventData>,
305    pub(crate) urgent_state_events: AsyncClosures<Address>,
306    pub(crate) minimize_events: AsyncClosures<MinimizeEventData>,
307    pub(crate) window_title_changed_events: AsyncClosures<Address>,
308    pub(crate) screencast_events: AsyncClosures<ScreencastEventData>,
309}
310
311/// Event data for renameworkspace event
312#[derive(Debug, Clone)]
313pub struct WorkspaceRenameEventData {
314    /// Workspace id
315    pub workspace_id: String,
316    /// Workspace name content
317    pub workspace_name: String,
318}
319
320/// Event data for a minimize event
321#[derive(Clone, Debug)]
322pub struct MinimizeEventData {
323    /// Window address
324    pub window_address: Address,
325    /// whether it's minimized or not
326    pub is_minimized: bool,
327}
328
329/// Event data for screencast event
330#[derive(Debug, Clone, Copy)]
331pub struct ScreencastEventData {
332    /// State/Is it turning on?
333    pub is_turning_on: bool,
334    /// Owner type, is it a monitor?
335    pub is_monitor: bool,
336}
337
338/// The data for the event executed when moving a window to a new workspace
339#[derive(Clone, Debug)]
340pub struct WindowMoveEvent {
341    /// Window address
342    pub window_address: Address,
343    /// The workspace name
344    pub workspace_name: String,
345}
346
347#[allow(unsafe_code)]
348unsafe impl Send for WindowMoveEvent {}
349#[allow(unsafe_code)]
350unsafe impl Sync for WindowMoveEvent {}
351/// The data for the event executed when opening a new window
352#[derive(Clone, Debug)]
353pub struct WindowOpenEvent {
354    /// Window address
355    pub window_address: Address,
356    /// The workspace name
357    pub workspace_name: String,
358    /// Window class
359    pub window_class: String,
360    /// Window title
361    pub window_title: String,
362}
363
364#[allow(unsafe_code)]
365unsafe impl Send for WindowOpenEvent {}
366#[allow(unsafe_code)]
367unsafe impl Sync for WindowOpenEvent {}
368/// The data for the event executed when changing keyboard layouts
369#[derive(Clone, Debug)]
370pub struct LayoutEvent {
371    /// Keyboard name
372    pub keyboard_name: String,
373    /// Layout name
374    pub layout_name: String,
375}
376
377#[allow(unsafe_code)]
378unsafe impl Send for LayoutEvent {}
379#[allow(unsafe_code)]
380unsafe impl Sync for LayoutEvent {}
381/// The mutable state available to Closures
382#[derive(PartialEq, Eq, Clone, Debug)]
383pub struct State {
384    /// The active workspace
385    pub active_workspace: WorkspaceType,
386    /// The active monitor
387    pub active_monitor: String,
388    /// The fullscreen state
389    pub fullscreen_state: bool,
390}
391
392#[allow(unsafe_code)]
393unsafe impl Send for State {}
394#[allow(unsafe_code)]
395unsafe impl Sync for State {}
396impl State {
397    /// Execute changes in state
398    pub async fn execute_state(self, old: State) -> crate::Result<Self> {
399        let state = self.clone();
400        if self != old {
401            use crate::dispatch::{Dispatch, DispatchType};
402            if old.fullscreen_state != state.fullscreen_state {
403                use crate::dispatch::FullscreenType;
404                Dispatch::call_async(DispatchType::ToggleFullscreen(FullscreenType::NoParam))
405                    .await?;
406            }
407            if old.active_workspace != state.active_workspace {
408                use crate::dispatch::WorkspaceIdentifierWithSpecial;
409                Dispatch::call_async(DispatchType::Workspace(match &state.active_workspace {
410                    WorkspaceType::Regular(name) => WorkspaceIdentifierWithSpecial::Name(name),
411                    WorkspaceType::Special(opt) => {
412                        WorkspaceIdentifierWithSpecial::Special(match opt {
413                            Some(name) => Some(name),
414                            None => None,
415                        })
416                    }
417                }))
418                .await?;
419            }
420            if old.active_monitor != state.active_monitor {
421                use crate::dispatch::MonitorIdentifier;
422                Dispatch::call_async(DispatchType::FocusMonitor(MonitorIdentifier::Name(
423                    &state.active_monitor,
424                )))
425                .await?;
426            };
427        }
428        Ok(state.clone())
429    }
430    /// Execute changes in state
431    pub fn execute_state_sync(self, old: State) -> crate::Result<Self> {
432        let state = self.clone();
433        if self != old {
434            use crate::dispatch::{Dispatch, DispatchType};
435            if old.fullscreen_state != state.fullscreen_state {
436                use crate::dispatch::FullscreenType;
437                Dispatch::call(DispatchType::ToggleFullscreen(FullscreenType::NoParam))?;
438            }
439            if old.active_workspace != state.active_workspace {
440                use crate::dispatch::WorkspaceIdentifierWithSpecial;
441                Dispatch::call(DispatchType::Workspace(match &state.active_workspace {
442                    WorkspaceType::Regular(name) => WorkspaceIdentifierWithSpecial::Name(name),
443                    WorkspaceType::Special(opt) => {
444                        WorkspaceIdentifierWithSpecial::Special(match opt {
445                            Some(name) => Some(name),
446                            None => None,
447                        })
448                    }
449                }))?;
450            }
451            if old.active_monitor != state.active_monitor {
452                use crate::dispatch::MonitorIdentifier;
453                Dispatch::call(DispatchType::FocusMonitor(MonitorIdentifier::Name(
454                    &state.active_monitor,
455                )))?;
456            };
457        }
458        Ok(state)
459    }
460}
461
462pub(crate) fn execute_closure<T: Clone>(f: &Closure<T>, val: &T) {
463    match f {
464        EventTypes::MutableState(_) => panic!("Using mutable handler with immutable listener"),
465        EventTypes::Regular(fun) => fun(val.clone()),
466    }
467}
468
469pub(crate) async fn execute_closure_async<T>(f: &AsyncClosure<T>, val: T) {
470    match f {
471        AsyncEventTypes::MutableState(_) => panic!("Using mutable handler with immutable listener"),
472        AsyncEventTypes::Regular(fun) => fun(val).await,
473    }
474}
475
476#[allow(dead_code)]
477pub(crate) async fn execute_closure_async_state<T: Clone>(
478    f: &AsyncClosure<T>,
479    val: &T,
480    state: &mut State,
481) {
482    match f {
483        AsyncEventTypes::MutableState(fun) => fun(val.clone(), state).await,
484        AsyncEventTypes::Regular(_) => panic!("Using mutable handler with immutable listener"),
485    }
486}
487pub(crate) async fn execute_closure_mut<T>(
488    state: State,
489    f: &Closure<T>,
490    val: T,
491) -> crate::Result<State> {
492    let old_state = state.clone();
493    let mut new_state = state.clone();
494    match f {
495        EventTypes::MutableState(fun) => fun(val, &mut new_state),
496        EventTypes::Regular(fun) => fun(val),
497    }
498
499    let new_state = new_state.execute_state(old_state).await?;
500    Ok(new_state)
501}
502
503#[allow(clippy::redundant_clone)]
504pub(crate) fn execute_closure_mut_sync<T>(
505    state: State,
506    f: &Closure<T>,
507    val: T,
508) -> crate::Result<State> {
509    let old_state = state.clone();
510    let mut new_state = state.clone();
511    match f {
512        EventTypes::MutableState(fun) => fun(val, &mut new_state),
513        EventTypes::Regular(fun) => fun(val),
514    }
515
516    let new_state = new_state.execute_state_sync(old_state)?;
517    Ok(new_state)
518}
519
520/// This tuple struct holds window event data
521#[derive(Debug, Clone)]
522pub struct WindowEventData {
523    /// The window class
524    pub window_class: String,
525    /// The window title
526    pub window_title: String,
527    /// The window address
528    pub window_address: Address,
529}
530
531#[allow(unsafe_code)]
532unsafe impl Send for WindowEventData {}
533#[allow(unsafe_code)]
534unsafe impl Sync for WindowEventData {}
535/// This tuple struct holds monitor event data
536#[derive(Debug, Clone)]
537pub struct MonitorEventData {
538    /// The monitor name
539    pub monitor_name: String,
540    /// The workspace
541    pub workspace: WorkspaceType,
542}
543
544#[allow(unsafe_code)]
545unsafe impl Send for MonitorEventData {}
546#[allow(unsafe_code)]
547unsafe impl Sync for MonitorEventData {}
548/// This tuple struct holds monitor event data
549#[derive(Debug, Clone)]
550pub struct WindowFloatEventData {
551    /// The window address
552    pub window_address: Address,
553    /// The float state
554    pub is_floating: bool,
555}
556
557#[allow(unsafe_code)]
558unsafe impl Send for WindowFloatEventData {}
559#[allow(unsafe_code)]
560unsafe impl Sync for WindowFloatEventData {}
561/// This enum holds every event type
562#[derive(Debug, Clone)]
563pub(crate) enum Event {
564    WorkspaceChanged(WorkspaceType),
565    WorkspaceDeleted(WorkspaceType),
566    WorkspaceAdded(WorkspaceType),
567    WorkspaceMoved(MonitorEventData),
568    WorkspaceRename(WorkspaceRenameEventData),
569    ActiveWindowChangedV1(Option<(String, String)>),
570    ActiveWindowChangedV2(Option<Address>),
571    ActiveWindowChangedMerged(Option<WindowEventData>),
572    ActiveMonitorChanged(MonitorEventData),
573    FullscreenStateChanged(bool),
574    MonitorAdded(String),
575    MonitorRemoved(String),
576    WindowOpened(WindowOpenEvent),
577    WindowClosed(Address),
578    WindowMoved(WindowMoveEvent),
579    LayoutChanged(LayoutEvent),
580    SubMapChanged(String),
581    LayerOpened(String),
582    LayerClosed(String),
583    FloatStateChanged(WindowFloatEventData),
584    UrgentStateChanged(Address),
585    Minimize(MinimizeEventData),
586    WindowTitleChanged(Address),
587    Screencast(ScreencastEventData),
588}
589
590fn check_for_regex_error(val: Result<Regex, RegexError>) -> Regex {
591    match val {
592        Ok(value) => value,
593        Err(RegexError::Syntax(str)) => panic!("syntax error: {str}"),
594        Err(RegexError::CompiledTooBig(size)) => {
595            panic!("The compiled regex size is too big ({size})")
596        }
597        Err(_) => unreachable!(),
598    }
599}
600
601fn parse_string_as_work(str: String) -> WorkspaceType {
602    if str == "special" {
603        WorkspaceType::Special(None)
604    } else if str.starts_with("special:") {
605        {
606            let mut iter = str.split(':');
607            iter.next();
608            match iter.next() {
609                Some(name) => WorkspaceType::Special(Some(name.to_string())),
610                None => WorkspaceType::Special(None),
611            }
612        }
613    } else {
614        WorkspaceType::Regular(str)
615    }
616}
617
618macro_rules! report_unknown {
619    ($event:tt) => {
620        #[cfg(not(feature = "silent"))]
621        eprintln!(
622            "A unknown event was passed into Hyprland-rs
623            PLEASE MAKE AN ISSUE!!
624            The event was: {event}",
625            event = $event
626        );
627    };
628}
629
630use std::collections::{BTreeSet, HashMap};
631use std::sync::Mutex;
632static CHECK_TABLE: Mutex<BTreeSet<String>> = Mutex::new(BTreeSet::new());
633
634#[derive(PartialEq, Eq, Hash)]
635enum ParsedEventType {
636    WorkspaceChanged,
637    WorkspaceDeleted,
638    WorkspaceAdded,
639    WorkspaceMoved,
640    WorkspaceRename,
641    ActiveWindowChangedV1,
642    ActiveWindowChangedV2,
643    ActiveMonitorChanged,
644    FullscreenStateChanged,
645    MonitorAdded,
646    MonitorRemoved,
647    WindowOpened,
648    WindowClosed,
649    WindowMoved,
650    LayoutChanged,
651    SubMapChanged,
652    LayerOpened,
653    LayerClosed,
654    FloatStateChanged,
655    UrgentStateChanged,
656    Minimize,
657    WindowTitleChanged,
658    Screencast,
659    Unknown,
660}
661
662/// This internal function parses event strings
663pub(crate) fn event_parser(event: String) -> crate::Result<Vec<Event>> {
664    lazy_static! {
665        static ref EVENT_SET: HashMap<ParsedEventType, Regex> = vec![
666            (
667                ParsedEventType::WorkspaceChanged,
668                r"\bworkspace>>(?P<workspace>.*)"
669            ),
670            (
671                ParsedEventType::WorkspaceDeleted,
672                r"destroyworkspace>>(?P<workspace>.*)"
673            ),
674            (
675                ParsedEventType::WorkspaceAdded,
676                r"createworkspace>>(?P<workspace>.*)"
677            ),
678            (
679                ParsedEventType::WorkspaceMoved,
680                r"moveworkspace>>(?P<workspace>.*),(?P<monitor>.*)"
681            ),
682            (
683                ParsedEventType::WorkspaceRename,
684                r"renameworkspace>>(?P<id>.*),(?P<name>.*)"
685            ),
686            (
687                ParsedEventType::ActiveMonitorChanged,
688                r"focusedmon>>(?P<monitor>.*),(?P<workspace>.*)"
689            ),
690            (
691                ParsedEventType::ActiveWindowChangedV1,
692                r"activewindow>>(?P<class>.*?),(?P<title>.*)"
693            ),
694            (
695                ParsedEventType::ActiveWindowChangedV2,
696                r"activewindowv2>>(?P<address>.*)"
697            ),
698            (
699                ParsedEventType::FullscreenStateChanged,
700                r"fullscreen>>(?P<state>0|1)"
701            ),
702            (
703                ParsedEventType::MonitorRemoved,
704                r"monitorremoved>>(?P<monitor>.*)"
705            ),
706            (
707                ParsedEventType::MonitorAdded,
708                r"monitoradded>>(?P<monitor>.*)"
709            ),
710            (
711                ParsedEventType::WindowOpened,
712                r"openwindow>>(?P<address>.*),(?P<workspace>.*),(?P<class>.*),(?P<title>.*)"
713            ),
714            (
715                ParsedEventType::WindowClosed,
716                r"closewindow>>(?P<address>.*)"
717            ),
718            (
719                ParsedEventType::WindowMoved,
720                r"movewindow>>(?P<address>.*),(?P<workspace>.*)"
721            ),
722            (
723                ParsedEventType::LayoutChanged,
724                r"activelayout>>(?P<keyboard>.*)(?P<layout>.*)"
725            ),
726            (ParsedEventType::SubMapChanged, r"submap>>(?P<submap>.*)"),
727            (
728                ParsedEventType::LayerOpened,
729                r"openlayer>>(?P<namespace>.*)"
730            ),
731            (
732                ParsedEventType::LayerClosed,
733                r"closelayer>>(?P<namespace>.*)"
734            ),
735            (
736                ParsedEventType::FloatStateChanged,
737                r"changefloatingmode>>(?P<address>.*),(?P<floatstate>[0-1])"
738            ),
739            (
740                ParsedEventType::Minimize,
741                r"minimize>>(?P<address>.*),(?P<state>[0-1])"
742            ),
743            (
744                ParsedEventType::Screencast,
745                r"screencast>>(?P<state>[0-1]),(?P<owner>[0-1])"
746            ),
747            (
748                ParsedEventType::UrgentStateChanged,
749                r"urgent>>(?P<address>.*)"
750            ),
751            (
752                ParsedEventType::WindowTitleChanged,
753                r"windowtitle>>(?P<address>.*)"
754            ),
755            (ParsedEventType::Unknown, r"(?P<Event>^[^>]*)"),
756        ]
757        .into_iter()
758        .map(|(e, r)| (e, check_for_regex_error(Regex::new(r))))
759        .collect();
760    }
761
762    let event_iter = event.trim().split('\n');
763
764    let mut events: Vec<Event> = vec![];
765
766    for item in event_iter {
767        let matched: Vec<_> = EVENT_SET
768            .iter()
769            .filter(|(_, r)| r.is_match(item))
770            .map(|(pet, r)| {
771                (
772                    pet,
773                    r.captures(item).unwrap_or_else(|| {
774                        panic!(
775                            "Unable to find captures while parsing Hyprland event: {}",
776                            item
777                        )
778                    }),
779                )
780            })
781            .collect();
782
783        let (e, captures) = match matched.len() {
784            0 => unreachable!(),
785            1 => {
786                report_unknown!((item.split('>').next().unwrap_or("unknown")));
787                continue;
788            }
789            2 => matched
790                .into_iter()
791                .find(|(e, _)| **e != ParsedEventType::Unknown)
792                .unwrap_or_else(|| unreachable!()),
793            _ => {
794                return Err(HyprError::IoError(io::Error::new(
795                    io::ErrorKind::InvalidData,
796                    "Event matched more than one regex (not a unknown event issue!)",
797                )));
798            }
799        };
800
801        match e {
802            ParsedEventType::WorkspaceChanged => {
803                let captured = &captures["workspace"];
804                let workspace = if !captured.is_empty() {
805                    parse_string_as_work(captured.to_string())
806                } else {
807                    WorkspaceType::Regular("1".to_string())
808                };
809                events.push(Event::WorkspaceChanged(workspace));
810            }
811            ParsedEventType::WorkspaceDeleted => {
812                let workspace = parse_string_as_work(captures["workspace"].to_string());
813                events.push(Event::WorkspaceDeleted(workspace));
814            }
815            ParsedEventType::WorkspaceAdded => {
816                let workspace = parse_string_as_work(captures["workspace"].to_string());
817                events.push(Event::WorkspaceAdded(workspace));
818            }
819            ParsedEventType::WorkspaceMoved => {
820                let workspace = parse_string_as_work(captures["workspace"].to_string());
821                let monitor = &captures["monitor"];
822                events.push(Event::WorkspaceMoved(MonitorEventData {
823                    monitor_name: monitor.to_string(),
824                    workspace,
825                }));
826            }
827            ParsedEventType::WorkspaceRename => {
828                let id = &captures["id"];
829                let name = &captures["name"];
830                events.push(Event::WorkspaceRename(WorkspaceRenameEventData {
831                    workspace_id: id.to_string(),
832                    workspace_name: name.to_string(),
833                }));
834            }
835            ParsedEventType::ActiveMonitorChanged => {
836                let monitor = &captures["monitor"];
837                let workspace = &captures["workspace"];
838                events.push(Event::ActiveMonitorChanged(MonitorEventData {
839                    monitor_name: monitor.to_string(),
840                    workspace: WorkspaceType::Regular(workspace.to_string()),
841                }));
842            }
843            ParsedEventType::ActiveWindowChangedV1 => {
844                let class = &captures["class"];
845                let title = &captures["title"];
846                if !class.is_empty() && !title.is_empty() {
847                    events.push(Event::ActiveWindowChangedV1(Some((
848                        class.to_string(),
849                        title.to_string(),
850                    ))));
851                } else {
852                    events.push(Event::ActiveWindowChangedV1(None));
853                }
854            }
855            ParsedEventType::ActiveWindowChangedV2 => {
856                let addr = format_event_addr(&captures["address"]);
857                if addr != "," {
858                    events.push(Event::ActiveWindowChangedV2(Some(Address::new(addr))));
859                } else {
860                    events.push(Event::ActiveWindowChangedV2(None));
861                }
862            }
863            ParsedEventType::FullscreenStateChanged => {
864                let state = &captures["state"] != "0";
865                events.push(Event::FullscreenStateChanged(state))
866            }
867            ParsedEventType::MonitorRemoved => {
868                let monitor = &captures["monitor"];
869                events.push(Event::MonitorRemoved(monitor.to_string()));
870            }
871            ParsedEventType::MonitorAdded => {
872                let monitor = &captures["monitor"];
873                events.push(Event::MonitorAdded(monitor.to_string()));
874            }
875            ParsedEventType::WindowOpened => {
876                let addr = format_event_addr(&captures["address"]);
877                let workspace = &captures["workspace"];
878                let class = &captures["class"];
879                let title = &captures["title"];
880                events.push(Event::WindowOpened(WindowOpenEvent {
881                    window_address: Address::new(addr),
882                    workspace_name: workspace.to_string(),
883                    window_class: class.to_string(),
884                    window_title: title.to_string(),
885                }));
886            }
887            ParsedEventType::WindowClosed => {
888                let addr = format_event_addr(&captures["address"]);
889                events.push(Event::WindowClosed(Address::new(addr)));
890            }
891            ParsedEventType::WindowMoved => {
892                let addr = format_event_addr(&captures["address"]);
893                let work = &captures["workspace"];
894                events.push(Event::WindowMoved(WindowMoveEvent {
895                    window_address: Address::new(addr),
896                    workspace_name: work.to_string(),
897                }));
898            }
899            ParsedEventType::LayoutChanged => {
900                let keyboard_name = &captures["keyboard"];
901                let layout = &captures["layout"];
902                events.push(Event::LayoutChanged(LayoutEvent {
903                    keyboard_name: keyboard_name.to_string(),
904                    layout_name: layout.to_string(),
905                }));
906            }
907            ParsedEventType::SubMapChanged => {
908                let submap = &captures["submap"];
909                events.push(Event::SubMapChanged(submap.to_string()));
910            }
911            ParsedEventType::LayerOpened => {
912                let namespace = &captures["namespace"];
913                events.push(Event::LayerOpened(namespace.to_string()));
914            }
915            ParsedEventType::LayerClosed => {
916                let namespace = &captures["namespace"];
917                events.push(Event::LayerClosed(namespace.to_string()));
918            }
919            ParsedEventType::FloatStateChanged => {
920                let addr = format_event_addr(&captures["address"]);
921                let state = &captures["floatstate"] == "0"; // FIXME: does 0 mean it's floating?
922                events.push(Event::FloatStateChanged(WindowFloatEventData {
923                    window_address: Address::new(addr),
924                    is_floating: state,
925                }));
926            }
927            ParsedEventType::Minimize => {
928                let addr = format_event_addr(&captures["address"]);
929                let state = &captures["state"] == "1";
930                events.push(Event::Minimize(MinimizeEventData {
931                    window_address: Address::new(addr),
932                    is_minimized: state,
933                }));
934            }
935            ParsedEventType::Screencast => {
936                let state = &captures["state"] == "1";
937                let owner = &captures["owner"] == "1";
938                events.push(Event::Screencast(ScreencastEventData {
939                    is_turning_on: state,
940                    is_monitor: owner,
941                }));
942            }
943            ParsedEventType::UrgentStateChanged => {
944                let addr = format_event_addr(&captures["address"]);
945                events.push(Event::UrgentStateChanged(Address::new(addr)));
946            }
947            ParsedEventType::WindowTitleChanged => {
948                let addr = format_event_addr(&captures["address"]);
949                events.push(Event::WindowTitleChanged(Address::new(addr)));
950            }
951            ParsedEventType::Unknown => {
952                #[cfg(not(feature = "silent"))]
953                match &captures.name("event") {
954                    Some(s) => {
955                        let table = CHECK_TABLE.lock();
956                        if let Ok(mut tbl) = table {
957                            let should_run = tbl.insert(s.as_str().to_string());
958                            if should_run {
959                                eprintln!(
960                                    "A unknown event was passed into Hyprland-rs
961                        PLEASE MAKE AN ISSUE!!
962                        The event was: {}",
963                                    s.as_str()
964                                );
965                            }
966                        }
967                    }
968                    None => {
969                        let table = CHECK_TABLE.lock();
970                        if let Ok(mut tbl) = table {
971                            let should_run = tbl.insert("unknown".to_string());
972                            if should_run {
973                                eprintln!(
974                            "A unknown event was passed into Hyprland-rs\nPLEASE MAKE AN ISSUE!!\nThe event was: {item}"
975                        );
976                            }
977                        }
978                    }
979                };
980            }
981        }
982    }
983
984    Ok(events)
985}
986
987fn format_event_addr(addr: &str) -> String {
988    format!("0x{addr}")
989}