1use crate::shared::*;
2use regex::{Error as RegexError, Regex};
3use std::fmt::Debug;
4use std::io;
5use std::pin::Pin;
6
7#[async_trait]
9pub(crate) trait Listener: HasExecutor {
10 fn start_listener() -> crate::Result<()>;
12}
13
14#[async_trait]
16pub(crate) trait AsyncListener: HasAsyncExecutor {
17 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), None, Empty, }
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#[derive(Debug, Clone)]
313pub struct WorkspaceRenameEventData {
314 pub workspace_id: String,
316 pub workspace_name: String,
318}
319
320#[derive(Clone, Debug)]
322pub struct MinimizeEventData {
323 pub window_address: Address,
325 pub is_minimized: bool,
327}
328
329#[derive(Debug, Clone, Copy)]
331pub struct ScreencastEventData {
332 pub is_turning_on: bool,
334 pub is_monitor: bool,
336}
337
338#[derive(Clone, Debug)]
340pub struct WindowMoveEvent {
341 pub window_address: Address,
343 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#[derive(Clone, Debug)]
353pub struct WindowOpenEvent {
354 pub window_address: Address,
356 pub workspace_name: String,
358 pub window_class: String,
360 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#[derive(Clone, Debug)]
370pub struct LayoutEvent {
371 pub keyboard_name: String,
373 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#[derive(PartialEq, Eq, Clone, Debug)]
383pub struct State {
384 pub active_workspace: WorkspaceType,
386 pub active_monitor: String,
388 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 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 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#[derive(Debug, Clone)]
522pub struct WindowEventData {
523 pub window_class: String,
525 pub window_title: String,
527 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#[derive(Debug, Clone)]
537pub struct MonitorEventData {
538 pub monitor_name: String,
540 pub workspace: WorkspaceType,
542}
543
544#[allow(unsafe_code)]
545unsafe impl Send for MonitorEventData {}
546#[allow(unsafe_code)]
547unsafe impl Sync for MonitorEventData {}
548#[derive(Debug, Clone)]
550pub struct WindowFloatEventData {
551 pub window_address: Address,
553 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#[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
662pub(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"; 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}