From cd47a74b158992bd716d5387aba8de58015b6e86 Mon Sep 17 00:00:00 2001 From: yavko Date: Sat, 24 Feb 2024 17:46:41 -0800 Subject: [PATCH 1/7] Fix horrible docs & Add active special events --- src/data/regular.rs | 2 ++ src/dispatch.rs | 16 +++++++++------- src/event_listener/async_im.rs | 4 ++++ src/event_listener/immutable.rs | 2 ++ src/event_listener/macros.rs | 18 ++++++++++-------- src/event_listener/mod.rs | 8 +++++--- src/event_listener/mutable.rs | 10 ++++++++++ src/event_listener/shared.rs | 31 ++++++++++++++++++++++++++++--- src/shared.rs | 2 +- 9 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/data/regular.rs b/src/data/regular.rs index a4b1e3a..dfb96a8 100644 --- a/src/data/regular.rs +++ b/src/data/regular.rs @@ -471,7 +471,9 @@ pub enum AnimationStyle { Slide, /// Vertical slide animation SlideVert, + /// Slide with fade animation SlideFade, + /// Vertical slide with fade animation SlideFadeVert, /// Popin animation (with percentage) PopIn(u8), diff --git a/src/dispatch.rs b/src/dispatch.rs index 7f5ec38..ae84bba 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -244,7 +244,7 @@ impl std::fmt::Display for WorkspaceIdentifier<'_> { } } -/// This enum is the params to MoveWindow dispatcher +/// This enum is the params to [DispatchType::MoveWindow] dispatcher #[derive(Debug, Clone)] pub enum WindowMove<'a> { /// Moves the window to a specified monitor @@ -325,13 +325,13 @@ pub enum DispatchType<'a> { MoveWindow(WindowMove<'a>), /// This dispatcher centers the active window CenterWindow, - /// This dispatcher resizes the active window using a [`Position`][Position] enum + /// This dispatcher resizes the active window using a [Position] enum ResizeActive(Position), - /// This dispatcher moves the active window using a [`Position`][Position] enum + /// This dispatcher moves the active window using a [Position] enum MoveActive(Position), - /// This dispatcher resizes the specified window using a [`Position`][Position] enum + /// This dispatcher resizes the specified window using a [Position] enum ResizeWindowPixel(Position, WindowIdentifier<'a>), - /// This dispatcher moves the specified window using a [`Position`][Position] enum + /// This dispatcher moves the specified window using a [Position] enum MoveWindowPixel(Position, WindowIdentifier<'a>), /// This dispatcher cycles windows using a specified direction CycleWindow(CycleDirection), @@ -438,7 +438,7 @@ pub enum LockType { ToggleLock, } -/// Param for [SwapWithMaster] dispatcher +/// Param for [DispatchType::SwapWithMaster] dispatcher #[derive(Debug, Clone, Display)] pub enum SwapWithMasterParam { /// New focus is the new master window @@ -452,7 +452,7 @@ pub enum SwapWithMasterParam { Auto, } -/// Param for [FocusMaster] dispatcher +/// Param for [DispatchType::FocusMaster] dispatcher #[derive(Debug, Clone, Display)] pub enum FocusMasterParam { /// Focus stays at master, (even if it was selected before) @@ -491,7 +491,9 @@ pub(crate) fn gen_dispatch_str(cmd: DispatchType, dispatch: bool) -> crate::Resu MoveToWorkspace(work, None) => format!("movetoworkspace{sep}{work}"), MoveToWorkspaceSilent(work, Some(win)) => format!("movetoworkspacesilent{sep}{work},{win}"), MoveToWorkspaceSilent(work, None) => format!("movetoworkspacesilent{sep}{work}"), + #[allow(deprecated)] MoveFocusedWindowToWorkspace(work) => format!("movetoworkspace{sep}{work}"), + #[allow(deprecated)] MoveFocusedWindowToWorkspaceSilent(work) => format!("movetoworkspacesilent{sep}{work}"), ToggleFloating(Some(v)) => format!("togglefloating{sep}{v}"), ToggleFloating(None) => "togglefloating".to_string(), diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index aa33eb6..3288289 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -64,6 +64,10 @@ impl HasAsyncExecutor for AsyncEventListener { Event::WindowClosed(addr) => arm_async!(addr.clone(), window_close_events, self), Event::WindowMoved(even) => arm_async!(even.clone(), window_moved_events, self), Event::WindowOpened(even) => arm_async!(even.clone(), window_open_events, self), + Event::SpecialRemoved(monitor) => { + arm_async!(monitor.clone(), special_removed_events, self) + } + Event::ChangedSpecial(data) => arm_async!(data.clone(), special_changed_events, self), Event::LayoutChanged(even) => { arm_async!(even.clone(), keyboard_layout_change_events, self) } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index df67271..6ae65fd 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -50,6 +50,8 @@ impl HasExecutor for EventListener { WindowClosed(addr) => arm!(addr, window_close_events, self), WindowMoved(even) => arm!(even, window_moved_events, self), WindowOpened(even) => arm!(even, window_open_events, self), + SpecialRemoved(monitor) => arm!(monitor, special_removed_events, self), + ChangedSpecial(data) => arm!(data, special_changed_events, self), LayoutChanged(even) => arm!(even, keyboard_layout_change_events, self), SubMapChanged(map) => arm!(map, sub_map_changed_events, self), LayerOpened(namespace) => arm!(namespace, layer_open_events, self), diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index 24c1a4f..b576f02 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -34,7 +34,7 @@ macro_rules! add_listener_reg { ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl EventListener { - doc_comment! { concat!("This methods adds a event which ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -50,7 +50,7 @@ listener.start_listener();"#), ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl EventListener { - doc_comment! { concat!("This methods adds a event which executes when ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -69,7 +69,7 @@ macro_rules! add_async_listener { ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl AsyncEventListener { - doc_comment! { concat!("This methods adds a event which ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -85,7 +85,7 @@ listener.start_listener();"#), ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl AsyncEventListener { - doc_comment! { concat!("This methods adds a event which executes when ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -105,7 +105,7 @@ macro_rules! add_mut_async_listener { ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl AsyncMutableEventListener { - doc_comment! { concat!("This methods adds a event which ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -121,7 +121,7 @@ listener.start_listener();"#), ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl AsyncMutableEventListener { - doc_comment! { concat!("This methods adds a event which executes when ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); @@ -140,7 +140,7 @@ macro_rules! mut_add_listener { ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl EventListenerMutable { - doc_comment! { concat!("This methods adds a event which ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListenerMutable as EventListener; let mut listener = EventListener::new(); @@ -156,7 +156,7 @@ listener.start_listener();"#), ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl EventListenerMutable { - doc_comment! { concat!("This methods adds a event which executes when ", $c, r#" + doc_comment! { concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListenerMutable as EventListener; let mut listener = EventListener::new(); @@ -283,6 +283,8 @@ macro_rules! init_events { window_open_events: vec![], window_close_events: vec![], window_moved_events: vec![], + special_removed_events: vec![], + special_changed_events: vec![], keyboard_layout_change_events: vec![], sub_map_changed_events: vec![], layer_open_events: vec![], diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index 8f9943e..3402356 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -19,21 +19,23 @@ mod immutable; )] pub use crate::event_listener::mutable::EventListener as EventListenerMutable; -add_listener!(workspace_change d, WorkspaceType, "on workspace change", "changed workspace to" => id); +add_listener!(workspace_change d, WorkspaceType, "a workspace is changed", "changed workspace to" => id); add_listener!(workspace_added, WorkspaceType, "a workspace is created", "workspace was added" => id); add_listener!(workspace_destroy ed, WorkspaceType, "a workspace is destroyed", "workspace was destroyed" => id); add_listener!(workspace_moved, MonitorEventData, "a workspace is moved", "workspace was moved" => id); add_listener!(workspace_rename, WorkspaceRenameEventData, "a workspace is renamed", "workspace was renamed" => id); add_listener!(active_monitor_change d, MonitorEventData, "the active monitor is changed", "Active monitor changed to" => data); add_listener!(active_window_change d, Option, "the active window is changed", "Active window changed" => data); -add_listener!(fullscreen_state_change d, bool, "the active monitor is changed", "Fullscreen is on" => state); +add_listener!(fullscreen_state_change d, bool, "the fullscreen state is changed", "Fullscreen is on" => state); add_listener!(monitor_added, String, "a new monitor is added", "Monitor added" => data); add_listener!(monitor_removed, String, "a monitor is removed", "Monitor removed" => data); add_listener!(window_open, WindowOpenEvent, "a window is opened", "Window opened" => data); add_listener!(window_close, Address, "a window is closed", "Window closed" => data); add_listener!(window_moved, WindowMoveEvent, "a window is moved", "Window moved" => data); +add_listener!(special_remove d, String, "a monitor's special workspace is removed", "Special Workspace removed" => monitor); +add_listener!(special_change d, MonitorEventData, "a monitor's special workspace is changed", "Special Workspace changed" => data); add_listener!(keyboard_layout_change, LayoutEvent, "the keyboard layout is changed", "Layout changed" => data); -add_listener!(sub_map_change d, String, "the sub map is changed", "Submap changed" => data); +add_listener!(sub_map_change d, String, "the submap is changed", "Submap changed" => data); add_listener!(layer_open, String, "a new layer is opened", "Layer opened" => data); add_listener!(layer_closed, String, "a layer is closed", "Layer closed" => data); add_listener!(float_state, WindowFloatEventData, "the float state of a window is changed", "Float state changed" => data); diff --git a/src/event_listener/mutable.rs b/src/event_listener/mutable.rs index da2b443..a68c436 100644 --- a/src/event_listener/mutable.rs +++ b/src/event_listener/mutable.rs @@ -92,6 +92,12 @@ impl HasExecutor for EventListener { Event::WindowClosed(addr) => mut_arm_sync!(addr.clone(), window_close_events, self), Event::WindowMoved(even) => mut_arm_sync!(even.clone(), window_moved_events, self), Event::WindowOpened(even) => mut_arm_sync!(even.clone(), window_open_events, self), + Event::SpecialRemoved(monitor) => { + mut_arm_sync!(monitor.clone(), special_removed_events, self) + } + Event::ChangedSpecial(data) => { + mut_arm_sync!(data.clone(), special_changed_events, self) + } Event::LayoutChanged(lay) => { mut_arm_sync!(lay.clone(), keyboard_layout_change_events, self) } @@ -191,6 +197,10 @@ impl EventListener { Event::WindowMoved(even) => mut_arm!(even.clone(), window_moved_events, self), Event::WindowOpened(even) => mut_arm!(even.clone(), window_open_events, self), Event::LayoutChanged(lay) => mut_arm!(lay.clone(), keyboard_layout_change_events, self), + Event::SpecialRemoved(monitor) => { + mut_arm!(monitor.clone(), special_removed_events, self) + } + Event::ChangedSpecial(data) => mut_arm!(data.clone(), special_changed_events, self), Event::SubMapChanged(map) => mut_arm!(map.clone(), sub_map_changed_events, self), Event::LayerOpened(even) => mut_arm!(even.clone(), layer_open_events, self), Event::LayerClosed(even) => mut_arm!(even.clone(), layer_closed_events, self), diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index 9a85019..906eae4 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -268,6 +268,8 @@ pub(crate) struct Events { pub(crate) fullscreen_state_changed_events: Closures, pub(crate) monitor_removed_events: Closures, pub(crate) monitor_added_events: Closures, + pub(crate) special_removed_events: Closures, + pub(crate) special_changed_events: Closures, pub(crate) keyboard_layout_change_events: Closures, pub(crate) sub_map_changed_events: Closures, pub(crate) window_open_events: Closures, @@ -294,6 +296,8 @@ pub(crate) struct AsyncEvents { pub(crate) fullscreen_state_changed_events: AsyncClosures, pub(crate) monitor_removed_events: AsyncClosures, pub(crate) monitor_added_events: AsyncClosures, + pub(crate) special_removed_events: AsyncClosures, + pub(crate) special_changed_events: AsyncClosures, pub(crate) keyboard_layout_change_events: AsyncClosures, pub(crate) sub_map_changed_events: AsyncClosures, pub(crate) window_open_events: AsyncClosures, @@ -532,7 +536,7 @@ pub struct WindowEventData { unsafe impl Send for WindowEventData {} #[allow(unsafe_code)] unsafe impl Sync for WindowEventData {} -/// This tuple struct holds monitor event data +/// This struct holds monitor event data #[derive(Debug, Clone)] pub struct MonitorEventData { /// The monitor name @@ -545,7 +549,8 @@ pub struct MonitorEventData { unsafe impl Send for MonitorEventData {} #[allow(unsafe_code)] unsafe impl Sync for MonitorEventData {} -/// This tuple struct holds monitor event data + +/// This struct holds monitor event data #[derive(Debug, Clone)] pub struct WindowFloatEventData { /// The window address @@ -576,6 +581,8 @@ pub(crate) enum Event { WindowOpened(WindowOpenEvent), WindowClosed(Address), WindowMoved(WindowMoveEvent), + SpecialRemoved(String), + ChangedSpecial(MonitorEventData), LayoutChanged(LayoutEvent), SubMapChanged(String), LayerOpened(String), @@ -647,6 +654,7 @@ enum ParsedEventType { WindowOpened, WindowClosed, WindowMoved, + ActiveSpecial, LayoutChanged, SubMapChanged, LayerOpened, @@ -723,6 +731,10 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { ParsedEventType::LayoutChanged, r"activelayout>>(?P.*)(?P.*)" ), + ( + ParsedEventType::ActiveSpecial, + r"activespecial>>(?P.*),(?P.*)" + ), (ParsedEventType::SubMapChanged, r"submap>>(?P.*)"), ( ParsedEventType::LayerOpened, @@ -793,7 +805,7 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { _ => { return Err(HyprError::IoError(io::Error::new( io::ErrorKind::InvalidData, - "Event matched more than one regex (not a unknown event issue!)", + "Event matched more than one regex (not an unknown event issue!)", ))); } }; @@ -898,6 +910,19 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { workspace_name: work.to_string(), })); } + ParsedEventType::ActiveSpecial => { + let work = &captures["workspace"]; + if work.is_empty() { + events.push(Event::SpecialRemoved(work.to_string())); + } else { + let workspace = parse_string_as_work(work.to_string()); + let monitor = &captures["monitor"]; + events.push(Event::ChangedSpecial(MonitorEventData { + monitor_name: monitor.to_string(), + workspace, + })); + } + } ParsedEventType::LayoutChanged => { let keyboard_name = &captures["keyboard"]; let layout = &captures["layout"]; diff --git a/src/shared.rs b/src/shared.rs index 4dadbe8..f3c2a86 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -3,7 +3,7 @@ //! This module provides shared private and public functions, structs, enum, and types pub use async_trait::async_trait; use derive_more::Display; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Serialize}; use std::env::{var, VarError}; use std::hash::{Hash, Hasher}; use std::{error, fmt, io}; From b1eb169ab7ccc5095afb579b7fcfd4f101b778ca Mon Sep 17 00:00:00 2001 From: diniamo Date: Mon, 15 Apr 2024 20:23:23 +0200 Subject: [PATCH 2/7] feat: implement configreloaded event --- src/event_listener/async_im.rs | 1 + src/event_listener/immutable.rs | 1 + src/event_listener/macros.rs | 1 + src/event_listener/mod.rs | 1 + src/event_listener/mutable.rs | 2 ++ src/event_listener/shared.rs | 11 ++++++++++- 6 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index 4022277..c8ec7d3 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -62,6 +62,7 @@ impl HasAsyncExecutor for AsyncEventListener { Event::Minimize(data) => arm_async!(data, minimize_events, self), Event::WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), Event::Screencast(data) => arm_async!(data, screencast_events, self), + Event::ConfigReloaded(empty) => arm_async!(empty, config_reloaded_events, self), } Ok(()) } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index bf38c93..7698ccd 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -55,6 +55,7 @@ impl HasExecutor for EventListener { Minimize(data) => arm!(data, minimize_events, self), WindowTitleChanged(addr) => arm!(addr, window_title_changed_events, self), Screencast(data) => arm!(data, screencast_events, self), + ConfigReloaded(empty) => arm!(empty, config_reloaded_events, self), } Ok(()) } diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index 16a73e5..d9628d4 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -286,6 +286,7 @@ macro_rules! init_events { minimize_events: vec![], window_title_changed_events: vec![], screencast_events: vec![], + config_reloaded_events: vec![], } }; } diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index 3402356..fef6f6b 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -43,3 +43,4 @@ add_listener!(urgent_state, Address, "the urgent state of a window is changed", add_listener!(minimize, MinimizeEventData, "the minimize state of a window is changed", "minimize state changed" => data); add_listener!(window_title_change d, Address, "a window title is changed", "A window title changed" => data); add_listener!(screencast, ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data); +add_listener!(config_reload ed, EmptyEventData, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); diff --git a/src/event_listener/mutable.rs b/src/event_listener/mutable.rs index 4597c67..edc1cba 100644 --- a/src/event_listener/mutable.rs +++ b/src/event_listener/mutable.rs @@ -76,6 +76,7 @@ impl HasExecutor for EventListener { mut_arm_sync!(addr, window_title_changed_events, self) } Event::Screencast(data) => mut_arm_sync!(data, screencast_events, self), + Event::ConfigReloaded(empty) => mut_arm_sync!(empty, config_reloaded_events, self), } Ok(()) } @@ -154,6 +155,7 @@ impl EventListener { Event::Minimize(data) => mut_arm!(data, minimize_events, self), Event::Screencast(data) => mut_arm!(data, screencast_events, self), Event::WindowTitleChanged(addr) => mut_arm!(addr, window_title_changed_events, self), + Event::ConfigReloaded(empty) => mut_arm!(empty, config_reloaded_events, self), } Ok(()) } diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index 3f304e6..c1a510d 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -284,6 +284,7 @@ pub(crate) struct Events { pub(crate) minimize_events: Closures, pub(crate) window_title_changed_events: Closures
, pub(crate) screencast_events: Closures, + pub(crate) config_reloaded_events: Closures, } #[allow(clippy::type_complexity)] @@ -312,6 +313,7 @@ pub(crate) struct AsyncEvents { pub(crate) minimize_events: AsyncClosures, pub(crate) window_title_changed_events: AsyncClosures
, pub(crate) screencast_events: AsyncClosures, + pub(crate) config_reloaded_events: AsyncClosures, } /// Event data for renameworkspace event @@ -542,6 +544,10 @@ pub struct WindowFloatEventData { pub is_floating: bool, } +/// This struct is for events that have no data +#[derive(Debug, Clone, Copy)] +pub struct EmptyEventData; + /// This enum holds every event type #[derive(Debug, Clone)] pub(crate) enum Event { @@ -571,6 +577,7 @@ pub(crate) enum Event { Minimize(MinimizeEventData), WindowTitleChanged(Address), Screencast(ScreencastEventData), + ConfigReloaded(EmptyEventData), } fn parse_string_as_work(str: String) -> WorkspaceType { @@ -640,6 +647,7 @@ enum ParsedEventType { Minimize, WindowTitleChanged, Screencast, + ConfigReloaded, Unknown, } @@ -739,6 +747,7 @@ static EVENT_SET: Lazy> = Lazy::new(|| { ParsedEventType::WindowTitleChanged, r"windowtitle>>(?P
.*)", ), + (ParsedEventType::ConfigReloaded, r"configreloaded>>"), (ParsedEventType::Unknown, r"(?P^[^>]*)"), ].into_iter() .map(|(e, r)| ( @@ -772,7 +781,6 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { .iter() .filter_map(|(event_type, regex)| Some((event_type, regex.captures(event_line)?))) .collect::>(); - (event_line, type_matches) }) .filter(|(_, b)| !b.is_empty()); @@ -932,6 +940,7 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { } ParsedEventType::UrgentStateChanged => Ok(Event::UrgentStateChanged(Address::fmt_new(&captures["address"]))), ParsedEventType::WindowTitleChanged => Ok(Event::WindowTitleChanged(Address::fmt_new(&captures["address"]))), + ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded(EmptyEventData)), ParsedEventType::Unknown => { #[cfg(not(feature = "silent"))] { From f85838bebb73f8df1f84263fd7dfe8305b6203fa Mon Sep 17 00:00:00 2001 From: yavko Date: Mon, 3 Jun 2024 23:17:48 -0700 Subject: [PATCH 3/7] make configreloaded not use a empty struct --- src/event_listener/async_im.rs | 2 +- src/event_listener/immutable.rs | 2 +- src/event_listener/macros.rs | 94 +++++++++++++++++++++------------ src/event_listener/mod.rs | 2 +- src/event_listener/mutable.rs | 4 +- src/event_listener/shared.rs | 28 +++++++--- 6 files changed, 85 insertions(+), 47 deletions(-) diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index c8ec7d3..02bd830 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -62,7 +62,7 @@ impl HasAsyncExecutor for AsyncEventListener { Event::Minimize(data) => arm_async!(data, minimize_events, self), Event::WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), Event::Screencast(data) => arm_async!(data, screencast_events, self), - Event::ConfigReloaded(empty) => arm_async!(empty, config_reloaded_events, self), + Event::ConfigReloaded => arm_async!(config_reloaded_events, self), } Ok(()) } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index 7698ccd..ca95988 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -55,7 +55,7 @@ impl HasExecutor for EventListener { Minimize(data) => arm!(data, minimize_events, self), WindowTitleChanged(addr) => arm!(addr, window_title_changed_events, self), Screencast(data) => arm!(data, screencast_events, self), - ConfigReloaded(empty) => arm!(empty, config_reloaded_events, self), + ConfigReloaded => arm!(config_reloaded_events, self), } Ok(()) } diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index d9628d4..43eaeb4 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -20,43 +20,71 @@ macro_rules! add_listener { add_listener_reg!($name $end,$f,$c,$c2 => $id); mut_add_listener!($name $end,$f,$c,$c2 => $id); add_async_listener!($name $end,$f,$c,$c2 => $id); - //add_mut_async_listener!($name $end,$f,$c,$c2 => $id); + }; + ($name:ident $end:ident,$c:literal,$c2:literal => $id:ident) => { + add_listener_reg!($name $end,$c,$c2 => $id); + add_async_listener!($name $end,$c,$c2 => $id); }; ($name:ident,$f:ty,$c:literal,$c2:literal => $id:ident) => { add_listener_reg!($name,$f,$c,$c2 => $id); mut_add_listener!($name,$f,$c,$c2 => $id); add_async_listener!($name,$f,$c,$c2 => $id); - //add_mut_async_listener!($name,$f,$c,$c2 => $id); + }; + ($name:ident,$c:literal,$c2:literal => $id:ident) => { + add_listener_reg!($name,$c,$c2 => $id); + add_async_listener!($name,$c,$c2 => $id); }; } -macro_rules! add_listener_reg { - ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { +macro_rules! add_listener_reg_raw { + ($name:ident,$list_name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { impl EventListener { - #[doc = concat!("This method adds an event which executes when", stringify!($c), r#" + #[doc = concat!("This method adds an event which executes when", stringify!($c), r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); listener.start_listener();"#)] - pub fn [](&mut self, f: impl Fn($f) + 'static) { - self.events.[<$name $end _events>].push(EventTypes::Regular(Box::new(f))); + pub fn [](&mut self, f: $f) { + self.events.[<$list_name _events>].push(EventTypes::Regular(Box::new(f))); } } } }; +} + +macro_rules! add_listener_reg { + ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + paste! { + add_listener_reg_raw!($name,[<$name $end>],impl Fn($f) + 'static,$c,$c2 => $id); + } + }; + ($name:ident $end:ident,$c:literal,$c2:expr => $id:ident) => { + paste! { + add_listener_reg_raw!($name,[<$name $end>],impl Fn() + 'static,$c,$c2 => $id); + } + }; ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + add_listener_reg_raw!($name,$name,impl Fn($f) + 'static,$c,$c2 => $id); + }; + ($name:ident,$c:literal,$c2:expr => $id:ident) => { + add_listener_reg_raw!($name,$name,impl Fn() + 'static,$c,$c2 => $id); + }; +} + +macro_rules! add_async_listener_raw { + ($name:ident,$list_name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { - impl EventListener { - #[doc = concat!("This methods adds an event which executes when ", $c, r#" + impl AsyncEventListener { + #[doc = concat!("This method adds an event which executes when ", $c, r#" ```rust, no_run use hyprland::event_listener::EventListener; let mut listener = EventListener::new(); listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); listener.start_listener();"#)] - pub fn [](&mut self, f: impl Fn($f) + 'static) { - self.events.[<$name _events>].push(EventTypes::Regular(Box::new(f))); + pub fn [](&mut self, f: $f) { + self.events.[<$list_name _events>].push(AsyncEventTypes::Regular(Box::pin(f))); } } } @@ -66,34 +94,20 @@ listener.start_listener();"#)] macro_rules! add_async_listener { ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { - impl AsyncEventListener { - #[doc = concat!("This method adds an event which executes when ", $c, r#" -```rust, no_run -use hyprland::event_listener::EventListener; -let mut listener = EventListener::new(); -listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); -listener.start_listener();"#)] - pub fn [](&mut self, f: impl Fn($f) -> VoidFuture + Send + Sync + 'static) { - self.events.[<$name $end _events>].push(AsyncEventTypes::Regular(Box::pin(f))); - } - } + add_async_listener_raw!($name,[<$name $end>],impl Fn($f) -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); } }; - ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + ($name:ident $end:ident,$c:literal,$c2:expr => $id:ident) => { paste! { - impl AsyncEventListener { - #[doc = concat!("This methods adds an event which executes when ", $c, r#" -```rust, no_run -use hyprland::event_listener::EventListener; -let mut listener = EventListener::new(); -listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); -listener.start_listener();"#)] - pub fn [](&mut self, f: impl Fn($f) -> VoidFuture + Send + Sync + 'static) { - self.events.[<$name _events>].push(AsyncEventTypes::Regular(Box::pin(f))); - } - } + add_async_listener_raw!($name,[<$name $end>],impl Fn() -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); } }; + ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + add_async_listener_raw!($name,$name,impl Fn($f) -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); + }; + ($name:ident,$c:literal,$c2:expr => $id:ident) => { + add_async_listener_raw!($name,$name,impl Fn() -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); + }; } #[allow(unused_macros)] @@ -224,6 +238,12 @@ macro_rules! arm { execute_closure(item, $val.clone()); } }}; + ($nam:ident,$se:ident) => {{ + let events = &$se.events.$nam; + for item in events.iter() { + execute_empty_closure(item); + } + }}; } macro_rules! arm_async { @@ -233,6 +253,12 @@ macro_rules! arm_async { execute_closure_async(item, $val.clone()).await; } }}; + ($nam:ident,$se:ident) => {{ + let events = &$se.events.$nam; + for item in events.iter() { + execute_empty_closure_async(item).await; + } + }}; } #[allow(unused_macros)] diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index fef6f6b..5e845b5 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -43,4 +43,4 @@ add_listener!(urgent_state, Address, "the urgent state of a window is changed", add_listener!(minimize, MinimizeEventData, "the minimize state of a window is changed", "minimize state changed" => data); add_listener!(window_title_change d, Address, "a window title is changed", "A window title changed" => data); add_listener!(screencast, ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data); -add_listener!(config_reload ed, EmptyEventData, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); +add_listener!(config_reload ed, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); diff --git a/src/event_listener/mutable.rs b/src/event_listener/mutable.rs index edc1cba..58bc8e7 100644 --- a/src/event_listener/mutable.rs +++ b/src/event_listener/mutable.rs @@ -76,7 +76,7 @@ impl HasExecutor for EventListener { mut_arm_sync!(addr, window_title_changed_events, self) } Event::Screencast(data) => mut_arm_sync!(data, screencast_events, self), - Event::ConfigReloaded(empty) => mut_arm_sync!(empty, config_reloaded_events, self), + Event::ConfigReloaded => unreachable!(), } Ok(()) } @@ -155,7 +155,7 @@ impl EventListener { Event::Minimize(data) => mut_arm!(data, minimize_events, self), Event::Screencast(data) => mut_arm!(data, screencast_events, self), Event::WindowTitleChanged(addr) => mut_arm!(addr, window_title_changed_events, self), - Event::ConfigReloaded(empty) => mut_arm!(empty, config_reloaded_events, self), + Event::ConfigReloaded => unreachable!(), } Ok(()) } diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index c1a510d..6821b27 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -252,10 +252,13 @@ pub(crate) type VoidFutureMut = std::pin::Pin + Send + 'static>>; pub(crate) type Closure = EventTypes; +pub(crate) type EmptyClosure = EventTypes; pub(crate) type AsyncClosure = AsyncEventTypes< dyn Sync + Send + Fn(T) -> VoidFuture, dyn Sync + Send + Fn(T, &mut State) -> VoidFutureMut, >; +pub(crate) type EmptyAsyncClosure = + AsyncEventTypes VoidFuture, dyn Sync + Send + Fn() -> VoidFutureMut>; pub(crate) type Closures = Vec>; pub(crate) type AsyncClosures = Vec>; @@ -284,7 +287,7 @@ pub(crate) struct Events { pub(crate) minimize_events: Closures, pub(crate) window_title_changed_events: Closures
, pub(crate) screencast_events: Closures, - pub(crate) config_reloaded_events: Closures, + pub(crate) config_reloaded_events: Vec, } #[allow(clippy::type_complexity)] @@ -313,7 +316,7 @@ pub(crate) struct AsyncEvents { pub(crate) minimize_events: AsyncClosures, pub(crate) window_title_changed_events: AsyncClosures
, pub(crate) screencast_events: AsyncClosures, - pub(crate) config_reloaded_events: AsyncClosures, + pub(crate) config_reloaded_events: Vec, } /// Event data for renameworkspace event @@ -451,6 +454,13 @@ impl State { } } +pub(crate) fn execute_empty_closure(f: &EmptyClosure) { + match f { + EventTypes::MutableState(_) => unreachable!(), + EventTypes::Regular(fun) => fun(), + } +} + pub(crate) fn execute_closure(f: &Closure, val: T) { match f { EventTypes::MutableState(_) => { @@ -459,6 +469,12 @@ pub(crate) fn execute_closure(f: &Closure, val: T) { EventTypes::Regular(fun) => fun(val), } } +pub(crate) async fn execute_empty_closure_async(f: &EmptyAsyncClosure) { + match f { + AsyncEventTypes::MutableState(_) => unreachable!(), + AsyncEventTypes::Regular(fun) => fun().await, + } +} pub(crate) async fn execute_closure_async(f: &AsyncClosure, val: T) { match f { @@ -544,10 +560,6 @@ pub struct WindowFloatEventData { pub is_floating: bool, } -/// This struct is for events that have no data -#[derive(Debug, Clone, Copy)] -pub struct EmptyEventData; - /// This enum holds every event type #[derive(Debug, Clone)] pub(crate) enum Event { @@ -577,7 +589,7 @@ pub(crate) enum Event { Minimize(MinimizeEventData), WindowTitleChanged(Address), Screencast(ScreencastEventData), - ConfigReloaded(EmptyEventData), + ConfigReloaded, } fn parse_string_as_work(str: String) -> WorkspaceType { @@ -940,7 +952,7 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { } ParsedEventType::UrgentStateChanged => Ok(Event::UrgentStateChanged(Address::fmt_new(&captures["address"]))), ParsedEventType::WindowTitleChanged => Ok(Event::WindowTitleChanged(Address::fmt_new(&captures["address"]))), - ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded(EmptyEventData)), + ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded), ParsedEventType::Unknown => { #[cfg(not(feature = "silent"))] { From a3348ff96d85524a32a615aa448fccd0b57b3e93 Mon Sep 17 00:00:00 2001 From: yavko Date: Tue, 4 Jun 2024 01:50:52 -0700 Subject: [PATCH 4/7] removed event type wrapper --- src/event_listener/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index 5c0ded5..d1fee75 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -28,7 +28,7 @@ let mut listener = EventListener::new(); listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); listener.start_listener();"#)] pub fn [](&mut self, f: $f) { - self.events.[<$list_name _events>].push(EventTypes::Regular(Box::new(f))); + self.events.[<$list_name _events>].push(Box::new(f)); } } } @@ -65,7 +65,7 @@ let mut listener = EventListener::new(); listener.add_"#, stringify!($name), r#"_handler(|"#, stringify!($id), r#"| println!(""#, $c2, ": {", stringify!($id), r#":#?}")); listener.start_listener();"#)] pub fn [](&mut self, f: $f) { - self.events.[<$list_name _events>].push(AsyncEventTypes::Regular(Box::pin(f))); + self.events.[<$list_name _events>].push(Box::pin(f)); } } } From 8423d77b18920013f705968eb94f6a8af5baee91 Mon Sep 17 00:00:00 2001 From: yavko Date: Tue, 4 Jun 2024 14:48:39 -0700 Subject: [PATCH 5/7] add `lockgroups` and `ignoregrouplock` events --- src/event_listener/async_im.rs | 6 ++++++ src/event_listener/immutable.rs | 4 ++++ src/event_listener/macros.rs | 2 ++ src/event_listener/mod.rs | 2 ++ src/event_listener/shared.rs | 12 ++++++++++++ 5 files changed, 26 insertions(+) diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index 428694c..e2abd5c 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -65,6 +65,12 @@ impl HasAsyncExecutor for AsyncEventListener { Event::WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), Event::Screencast(data) => arm_async!(data, screencast_events, self), Event::ConfigReloaded => arm_async!(config_reloaded_events, self), + Event::IgnoreGroupLockStateChanged(bool) => { + arm_async!(bool, ignore_group_lock_state_changed_events, self) + } + Event::LockGroupsStateChanged(bool) => { + arm_async!(bool, lock_groups_state_changed_events, self) + } } Ok(()) } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index 63bad4c..84da228 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -56,6 +56,10 @@ impl HasExecutor for EventListener { WindowTitleChanged(addr) => arm!(addr, window_title_changed_events, self), Screencast(data) => arm!(data, screencast_events, self), ConfigReloaded => arm!(config_reloaded_events, self), + IgnoreGroupLockStateChanged(bool) => { + arm!(bool, ignore_group_lock_state_changed_events, self) + } + LockGroupsStateChanged(bool) => arm!(bool, lock_groups_state_changed_events, self), } Ok(()) } diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index d1fee75..4e67050 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -149,6 +149,8 @@ macro_rules! init_events { window_title_changed_events: vec![], screencast_events: vec![], config_reloaded_events: vec![], + ignore_group_lock_state_changed_events: vec![], + lock_groups_state_changed_events: vec![], } }; } diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index d6a22cd..1ce309b 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -37,3 +37,5 @@ add_listener!(minimize, MinimizeEventData, "the minimize state of a window is ch add_listener!(window_title_change d, Address, "a window title is changed", "A window title changed" => data); add_listener!(screencast, ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data); add_listener!(config_reload ed, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); +add_listener!(ignore_group_lock_state_change d, bool, "the state of ignore group lock is toggled", "ignore group lock toggled to" => data); +add_listener!(lock_groups_state_change d, bool, "the state of lock groups is toggled ", "lock group state toggled to" => data); diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index 527b660..7d53b99 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -252,6 +252,8 @@ pub(crate) struct Events { pub(crate) window_title_changed_events: Closures
, pub(crate) screencast_events: Closures, pub(crate) config_reloaded_events: Vec, + pub(crate) ignore_group_lock_state_changed_events: Closures, + pub(crate) lock_groups_state_changed_events: Closures } #[allow(clippy::type_complexity)] @@ -281,6 +283,8 @@ pub(crate) struct AsyncEvents { pub(crate) window_title_changed_events: AsyncClosures
, pub(crate) screencast_events: AsyncClosures, pub(crate) config_reloaded_events: Vec, + pub(crate) ignore_group_lock_state_changed_events: AsyncClosures, + pub(crate) lock_groups_state_changed_events: AsyncClosures } /// Event data for destroyworkspacev2 event @@ -501,6 +505,8 @@ pub(crate) enum Event { WindowTitleChanged(Address), Screencast(ScreencastEventData), ConfigReloaded, + IgnoreGroupLockStateChanged(bool), + LockGroupsStateChanged(bool) } fn parse_string_as_work(str: String) -> WorkspaceType { @@ -571,6 +577,8 @@ enum ParsedEventType { WindowTitleChanged, Screencast, ConfigReloaded, + IgnoreGroupLock, + LockGroups, Unknown, } @@ -671,6 +679,8 @@ static EVENT_SET: Lazy> = Lazy::new(|| { r"windowtitle>>(?P
.*)", ), (ParsedEventType::ConfigReloaded, r"configreloaded>>"), + (ParsedEventType::IgnoreGroupLock, r"ignoregrouplock>>(?P[0-1])"), + (ParsedEventType::LockGroups, r"lockgroups>>(?P[0-1])"), (ParsedEventType::Unknown, r"(?P^[^>]*)"), ].into_iter() .map(|(e, r)| ( @@ -861,6 +871,8 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { ParsedEventType::UrgentStateChanged => Ok(Event::UrgentStateChanged(Address::fmt_new(&captures["address"]))), ParsedEventType::WindowTitleChanged => Ok(Event::WindowTitleChanged(Address::fmt_new(&captures["address"]))), ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded), + ParsedEventType::IgnoreGroupLock => Ok(Event::IgnoreGroupLockStateChanged(&captures["state"] == "1")), + ParsedEventType::LockGroups => Ok(Event::LockGroupsStateChanged(&captures["state"] == "1")), ParsedEventType::Unknown => { #[cfg(not(feature = "silent"))] { From e204ad90273d888bd1f7abf0d29a5a3bffba9fb5 Mon Sep 17 00:00:00 2001 From: yavko Date: Tue, 4 Jun 2024 15:37:32 -0700 Subject: [PATCH 6/7] add `pin` event --- src/event_listener/async_im.rs | 66 ++++++++++++++++----------------- src/event_listener/immutable.rs | 1 + src/event_listener/macros.rs | 1 + src/event_listener/mod.rs | 3 +- src/event_listener/shared.rs | 38 +++++++++++-------- 5 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index e2abd5c..9d8b2b6 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -29,48 +29,44 @@ impl Default for AsyncEventListener { impl HasAsyncExecutor for AsyncEventListener { async fn event_executor_async(&mut self, event: Event) -> crate::Result<()> { + use Event::*; match event { - Event::WorkspaceChanged(id) => arm_async!(id, workspace_changed_events, self), - Event::WorkspaceAdded(id) => arm_async!(id, workspace_added_events, self), - Event::WorkspaceDeleted(data) => { - arm_async!(data, workspace_destroyed_events, self) - } - Event::WorkspaceMoved(evend) => arm_async!(evend, workspace_moved_events, self), - Event::WorkspaceRename(even) => arm_async!(even, workspace_rename_events, self), - Event::ActiveMonitorChanged(evend) => { - arm_async!(evend, active_monitor_changed_events, self) - } - Event::ActiveWindowChangedMerged(event) => { + WorkspaceChanged(id) => arm_async!(id, workspace_changed_events, self), + WorkspaceAdded(id) => arm_async!(id, workspace_added_events, self), + WorkspaceDeleted(data) => arm_async!(data, workspace_destroyed_events, self), + WorkspaceMoved(evend) => arm_async!(evend, workspace_moved_events, self), + WorkspaceRename(even) => arm_async!(even, workspace_rename_events, self), + ActiveMonitorChanged(evend) => arm_async!(evend, active_monitor_changed_events, self), + ActiveWindowChangedMerged(event) => { arm_async!(event, active_window_changed_events, self) } - Event::ActiveWindowChangedV1(_) => (), - Event::ActiveWindowChangedV2(_) => (), - Event::FullscreenStateChanged(bool) => { - arm_async!(bool, fullscreen_state_changed_events, self) - } - Event::MonitorAdded(monitor) => arm_async!(monitor, monitor_added_events, self), - Event::MonitorRemoved(monitor) => arm_async!(monitor, monitor_removed_events, self), - Event::WindowClosed(addr) => arm_async!(addr, window_close_events, self), - Event::WindowMoved(even) => arm_async!(even, window_moved_events, self), - Event::WindowOpened(even) => arm_async!(even, window_open_events, self), - Event::SpecialRemoved(monitor) => arm_async!(monitor, special_removed_events, self), - Event::ChangedSpecial(data) => arm_async!(data, special_changed_events, self), - Event::LayoutChanged(even) => arm_async!(even, keyboard_layout_change_events, self), - Event::SubMapChanged(map) => arm_async!(map, sub_map_changed_events, self), - Event::LayerOpened(namespace) => arm_async!(namespace, layer_open_events, self), - Event::LayerClosed(namespace) => arm_async!(namespace, layer_closed_events, self), - Event::FloatStateChanged(even) => arm_async!(even, float_state_events, self), - Event::UrgentStateChanged(even) => arm_async!(even, urgent_state_events, self), - Event::Minimize(data) => arm_async!(data, minimize_events, self), - Event::WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), - Event::Screencast(data) => arm_async!(data, screencast_events, self), - Event::ConfigReloaded => arm_async!(config_reloaded_events, self), - Event::IgnoreGroupLockStateChanged(bool) => { + ActiveWindowChangedV1(_) => (), + ActiveWindowChangedV2(_) => (), + FullscreenStateChanged(bool) => arm_async!(bool, fullscreen_state_changed_events, self), + MonitorAdded(monitor) => arm_async!(monitor, monitor_added_events, self), + MonitorRemoved(monitor) => arm_async!(monitor, monitor_removed_events, self), + WindowClosed(addr) => arm_async!(addr, window_close_events, self), + WindowMoved(even) => arm_async!(even, window_moved_events, self), + WindowOpened(even) => arm_async!(even, window_open_events, self), + SpecialRemoved(monitor) => arm_async!(monitor, special_removed_events, self), + ChangedSpecial(data) => arm_async!(data, special_changed_events, self), + LayoutChanged(even) => arm_async!(even, keyboard_layout_change_events, self), + SubMapChanged(map) => arm_async!(map, sub_map_changed_events, self), + LayerOpened(namespace) => arm_async!(namespace, layer_open_events, self), + LayerClosed(namespace) => arm_async!(namespace, layer_closed_events, self), + FloatStateChanged(even) => arm_async!(even, float_state_events, self), + UrgentStateChanged(even) => arm_async!(even, urgent_state_events, self), + Minimize(data) => arm_async!(data, minimize_events, self), + WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), + Screencast(data) => arm_async!(data, screencast_events, self), + ConfigReloaded => arm_async!(config_reloaded_events, self), + IgnoreGroupLockStateChanged(bool) => { arm_async!(bool, ignore_group_lock_state_changed_events, self) } - Event::LockGroupsStateChanged(bool) => { + LockGroupsStateChanged(bool) => { arm_async!(bool, lock_groups_state_changed_events, self) } + WindowPinned(data) => arm_async!(data, window_pin_state_toggled_events, self), } Ok(()) } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index 84da228..9fe0fce 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -60,6 +60,7 @@ impl HasExecutor for EventListener { arm!(bool, ignore_group_lock_state_changed_events, self) } LockGroupsStateChanged(bool) => arm!(bool, lock_groups_state_changed_events, self), + WindowPinned(data) => arm!(data, window_pin_state_toggled_events, self), } Ok(()) } diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index 4e67050..89c4b57 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -151,6 +151,7 @@ macro_rules! init_events { config_reloaded_events: vec![], ignore_group_lock_state_changed_events: vec![], lock_groups_state_changed_events: vec![], + window_pin_state_toggled_events: vec![], } }; } diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index 1ce309b..9cd616b 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -38,4 +38,5 @@ add_listener!(window_title_change d, Address, "a window title is changed", "A wi add_listener!(screencast, ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data); add_listener!(config_reload ed, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); add_listener!(ignore_group_lock_state_change d, bool, "the state of ignore group lock is toggled", "ignore group lock toggled to" => data); -add_listener!(lock_groups_state_change d, bool, "the state of lock groups is toggled ", "lock group state toggled to" => data); +add_listener!(lock_groups_state_change d, bool, "the state of lock groups is toggled", "lock group state toggled to" => data); +add_listener!(window_pin_state_toggle d, WindowPinEventData, "the pinned state of a window is changed", "window pin was set to" => state); diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index 7d53b99..99c1719 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -3,18 +3,6 @@ use once_cell::sync::Lazy; use regex::{Error as RegexError, Regex}; use std::{fmt::Debug, pin::Pin}; -/// This trait provides shared behaviour for listener types -pub(crate) trait Listener: HasExecutor { - /// This method starts the event listener - fn start_listener() -> crate::Result<()>; -} - -/// This trait provides shared behaviour for listener types -pub(crate) trait AsyncListener: HasAsyncExecutor { - /// This method starts the event listener (async) - async fn start_listener_async() -> crate::Result<()>; -} - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum ActiveWindowValue { Queued(T), // aka Some(T) @@ -253,7 +241,8 @@ pub(crate) struct Events { pub(crate) screencast_events: Closures, pub(crate) config_reloaded_events: Vec, pub(crate) ignore_group_lock_state_changed_events: Closures, - pub(crate) lock_groups_state_changed_events: Closures + pub(crate) lock_groups_state_changed_events: Closures, + pub(crate) window_pin_state_toggled_events: Closures, } #[allow(clippy::type_complexity)] @@ -284,7 +273,8 @@ pub(crate) struct AsyncEvents { pub(crate) screencast_events: AsyncClosures, pub(crate) config_reloaded_events: Vec, pub(crate) ignore_group_lock_state_changed_events: AsyncClosures, - pub(crate) lock_groups_state_changed_events: AsyncClosures + pub(crate) lock_groups_state_changed_events: AsyncClosures, + pub(crate) window_pin_state_toggled_events: AsyncClosures, } /// Event data for destroyworkspacev2 event @@ -466,7 +456,7 @@ pub struct MonitorEventData { pub workspace: WorkspaceType, } -/// This struct holds monitor event data +/// This struct holds window float event data #[derive(Debug, Clone, PartialEq, Eq)] pub struct WindowFloatEventData { /// The window address @@ -475,6 +465,15 @@ pub struct WindowFloatEventData { pub is_floating: bool, } +/// This struct holds window pin event data +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WindowPinEventData { + /// The window address + pub window_address: Address, + /// The pin state + pub is_pinned: bool, +} + /// This enum holds every event type #[derive(Debug, Clone)] pub(crate) enum Event { @@ -506,7 +505,8 @@ pub(crate) enum Event { Screencast(ScreencastEventData), ConfigReloaded, IgnoreGroupLockStateChanged(bool), - LockGroupsStateChanged(bool) + LockGroupsStateChanged(bool), + WindowPinned(WindowPinEventData) } fn parse_string_as_work(str: String) -> WorkspaceType { @@ -579,6 +579,7 @@ enum ParsedEventType { ConfigReloaded, IgnoreGroupLock, LockGroups, + Pin, Unknown, } @@ -681,6 +682,7 @@ static EVENT_SET: Lazy> = Lazy::new(|| { (ParsedEventType::ConfigReloaded, r"configreloaded>>"), (ParsedEventType::IgnoreGroupLock, r"ignoregrouplock>>(?P[0-1])"), (ParsedEventType::LockGroups, r"lockgroups>>(?P[0-1])"), + (ParsedEventType::Pin, r"pin>>(?P
.*),(?P[0-1])"), (ParsedEventType::Unknown, r"(?P^[^>]*)"), ].into_iter() .map(|(e, r)| ( @@ -873,6 +875,10 @@ pub(crate) fn event_parser(event: String) -> crate::Result> { ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded), ParsedEventType::IgnoreGroupLock => Ok(Event::IgnoreGroupLockStateChanged(&captures["state"] == "1")), ParsedEventType::LockGroups => Ok(Event::LockGroupsStateChanged(&captures["state"] == "1")), + ParsedEventType::Pin => Ok(Event::WindowPinned(WindowPinEventData { + window_address: Address::fmt_new(&captures["address"]), + is_pinned: &captures["state"] == "1" + })), ParsedEventType::Unknown => { #[cfg(not(feature = "silent"))] { From b4ff36aeb237fe33be9f8e64b356f172897e83fa Mon Sep 17 00:00:00 2001 From: yavko Date: Mon, 2 Sep 2024 20:04:34 -0700 Subject: [PATCH 7/7] Mostly finish `0.4` --- Cargo.lock | 166 ++++-- Cargo.toml | 21 +- README.md | 4 +- hyprland-macros/Cargo.toml | 1 + hyprland-macros/src/lib.rs | 101 +++- src/ctl.rs | 43 ++ src/event_listener/async_im.rs | 54 +- src/event_listener/immutable.rs | 43 +- src/event_listener/macros.rs | 176 +++--- src/event_listener/mod.rs | 72 ++- src/event_listener/shared.rs | 941 ++++++++++++++++---------------- src/event_listener/stream.rs | 73 +++ src/lib.rs | 5 +- src/shared.rs | 11 +- 14 files changed, 979 insertions(+), 732 deletions(-) create mode 100644 src/event_listener/stream.rs diff --git a/Cargo.lock b/Cargo.lock index 512f954..f02aa81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,25 +30,16 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "almost_everything" -version = "0.4.0-alpha.2" +version = "0.4.0-beta.1" dependencies = [ "hyprland", ] [[package]] name = "almost_everything_async" -version = "0.4.0-alpha.2" +version = "0.4.0-beta.1" dependencies = [ "hyprland", "tokio", @@ -202,6 +193,28 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "async-task" version = "4.7.1" @@ -311,6 +324,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "errno" version = "0.3.8" @@ -433,6 +452,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + [[package]] name = "gimli" version = "0.28.1" @@ -459,29 +484,33 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hyprland" -version = "0.4.0-alpha.2" +version = "0.4.0-beta.1" dependencies = [ "ahash", "async-net", "async-std", + "async-stream", "derive_more", + "either", "futures-lite 2.3.0", "hyprland-macros", "num-traits", "once_cell", "parking_lot", "paste", - "regex", + "phf", "serde", "serde_json", "serde_repr", "tokio", + "tokio-stream", ] [[package]] name = "hyprland-macros" -version = "0.4.0-alpha.2" +version = "0.4.0-beta.1" dependencies = [ + "proc-macro2", "quote", "syn 2.0.61", ] @@ -665,6 +694,48 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -738,43 +809,29 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "bitflags 2.5.0", + "rand_core", ] [[package]] -name = "regex" -version = "1.10.4" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] -name = "regex-automata" -version = "0.4.6" +name = "redox_syscall" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "bitflags 2.5.0", ] -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -862,6 +919,12 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -948,6 +1011,31 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tracing" version = "0.1.40" diff --git a/Cargo.toml b/Cargo.toml index f456dde..5c1d675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ members = [ ] [workspace.package] -version = "0.4.0-alpha.2" +version = "0.4.0-beta.1" license = "GPL-3.0-or-later" repository = "https://github.com/hyprland-community/hyprland-rs" keywords = ["hyprland", "ipc", "hypr", "wayland", "linux"] @@ -37,7 +37,7 @@ categories = ["api-bindings"] authors = ["yavko "] [dependencies] -hyprland-macros = { path = "hyprland-macros", version = "0.4.0-alpha.1" } +hyprland-macros = { path = "hyprland-macros", version = "0.4.0-beta.1" } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_repr = "0.1" @@ -45,15 +45,12 @@ tokio = { version = "1", features = [ "io-std", "io-util", "net", + "sync" ], optional = true } +tokio-stream = { version = "0.1", features = ["sync"], optional = true } async-net = { version = "2.0", optional = true } async-std = { version = "1.12", optional = true } -futures-lite = { version = "2.3", optional = true, default-features = false } -regex = { version = "1.10", default-features = false, features = [ - "std", - "perf", - "unicode", -] } +futures-lite = { version = "2.3", default-features = false } num-traits = "0.2.19" paste = "1.0.14" derive_more = { version = "0.99", default-features = false, features = [ @@ -67,6 +64,9 @@ ahash = { version = "0.8", features = [ "no-rng", "serde", ], optional = true, default-features = false } +phf = {version="0.11.2", features = ["macros"] } +either = "1.13.0" +async-stream = "0.3.5" [features] default = [ @@ -79,8 +79,8 @@ default = [ "tokio", "ahash", ] -async-lite = ["dep:async-net", "dep:futures-lite"] -async-std = ["dep:async-std", "dep:futures-lite"] +async-lite = ["dep:async-net"] +async-std = ["dep:async-std"] tokio = ["dep:tokio"] dispatch = [] data = [] @@ -88,7 +88,6 @@ ctl = [] keyword = [] config = ["dispatch", "keyword"] listener = ["data", "dispatch"] -silent = [] parking_lot = ["dep:parking_lot", "once_cell/parking_lot", "tokio?/parking_lot"] ahash = ["dep:ahash"] unsafe-impl = [] diff --git a/README.md b/README.md index a457cc0..2b063c6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ An unofficial rust wrapper for Hyprland's IPC ## Help Wanted! We need help with developing the next version of hyprland-rs `0.4`, -if you know how to do the things in https://github.com/hyprland-community/hyprland-rs/milestone/4 +if you know how to do the things in contributions in those areas would be greatly appreciated! ## Disclaimer @@ -28,7 +28,7 @@ Let's get started with Hyprland-rs! Add the code below to the dependencies section of your Cargo.toml file! ```toml -hyprland = "0.3.13" +hyprland = "0.4.0-beta.1" ``` ### Reading the docs diff --git a/hyprland-macros/Cargo.toml b/hyprland-macros/Cargo.toml index 11d7067..4683069 100644 --- a/hyprland-macros/Cargo.toml +++ b/hyprland-macros/Cargo.toml @@ -17,3 +17,4 @@ proc-macro = true [dependencies] syn = { version = "2", features = ["full", "parsing"]} quote = "1" +proc-macro2 = "1" diff --git a/hyprland-macros/src/lib.rs b/hyprland-macros/src/lib.rs index 7bb974b..f0cfcdc 100644 --- a/hyprland-macros/src/lib.rs +++ b/hyprland-macros/src/lib.rs @@ -2,8 +2,12 @@ #![deny(missing_docs)] use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, ExprClosure}; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, Block, ExprClosure, Result, Token, Type, +}; /// Creates a async closure #[proc_macro] @@ -18,3 +22,96 @@ pub fn async_closure(input: TokenStream) -> TokenStream { }}; expanded.into() } + +struct If { + type_to_match: Type, + input_type: Type, + true_branch: T, + false_branch: T, +} + +impl Parse for If { + fn parse(input: ParseStream) -> Result { + let type_to_match: Type = input.parse()?; + input.parse::()?; + let input_type: Type = input.parse()?; + input.parse::()?; + let true_branch: T = input.parse()?; + input.parse::()?; + let false_branch: T = input.parse()?; + Ok(If { + type_to_match, + input_type, + true_branch, + false_branch, + }) + } +} + +/// Creates a compile time if statement +/// that takes checks if 2 types are the same +/// and if returns one of the branches +#[proc_macro] +pub fn block_if(input: TokenStream) -> TokenStream { + let If { + type_to_match, + input_type, + true_branch, + false_branch, + } = parse_macro_input!(input as If); + let used_branch = if type_to_match.to_token_stream().to_string() + == input_type.to_token_stream().to_string() + { + true_branch + } else { + false_branch + } + .stmts; + let mut strm = TokenStream2::new(); + for stmt in used_branch { + stmt.to_tokens(&mut strm) + } + strm.into() +} + +/// Creates a compile time if statement +/// that takes checks if 2 types are the same +/// and if returns one of the branches +#[proc_macro] +pub fn type_if(input: TokenStream) -> TokenStream { + let If { + type_to_match, + input_type, + true_branch, + false_branch, + } = parse_macro_input!(input as If); + let used_branch = if type_to_match.to_token_stream().to_string() + == input_type.to_token_stream().to_string() + { + true_branch + } else { + false_branch + }; + used_branch.into_token_stream().into() +} + +/// Creates a compile time if statement +/// that takes checks if 2 types are the same +/// and if returns one of the branches +#[proc_macro] +pub fn expr_if(input: TokenStream) -> TokenStream { + let If { + type_to_match, + input_type, + true_branch, + false_branch, + } = parse_macro_input!(input as If); + let used_branch = if type_to_match.to_token_stream().to_string() + == input_type.to_token_stream().to_string() + { + true_branch + } else { + false_branch + }; + used_branch.into_token_stream().into() +} diff --git a/src/ctl.rs b/src/ctl.rs index 977aece..5162007 100644 --- a/src/ctl.rs +++ b/src/ctl.rs @@ -200,6 +200,49 @@ pub mod notify { Ok(()) } } +/// Dismisses all or up to a specified amount of notifications with Hyprland +pub mod dismissnotify { + use std::num::NonZeroU8; + + use super::*; + /// Dismisses notifications with Hyprland + /// + /// If `amount` is [None] then will dismiss ALL notifications + pub fn call(amount: Option) -> crate::Result<()> { + write_to_socket_sync( + SocketType::Command, + command!( + Empty, + "dismissnotify {}", + if let Some(amount) = amount { + amount.to_string() + } else { + (-1).to_string() + } + ), + )?; + Ok(()) + } + /// Dismisses notifications with Hyprland (async) + /// + /// If `amount` is [None] then will dismiss ALL notifications + pub async fn call_async(amount: Option) -> crate::Result<()> { + write_to_socket( + SocketType::Command, + command!( + Empty, + "dismissnotify {}", + if let Some(amount) = amount { + amount.to_string() + } else { + (-1).to_string() + } + ), + ) + .await?; + Ok(()) + } +} /// A 8-bit color with a alpha channel #[derive(Debug, Copy, Clone, MDisplay, Constructor, PartialEq, Eq)] diff --git a/src/event_listener/async_im.rs b/src/event_listener/async_im.rs index 9d8b2b6..fe16b6b 100644 --- a/src/event_listener/async_im.rs +++ b/src/event_listener/async_im.rs @@ -1,6 +1,4 @@ -use crate::shared::*; - -use crate::event_listener::shared::*; +use super::*; /// This struct is used for adding event handlers and executing them on events /// # The Event Listener @@ -27,51 +25,6 @@ impl Default for AsyncEventListener { } } -impl HasAsyncExecutor for AsyncEventListener { - async fn event_executor_async(&mut self, event: Event) -> crate::Result<()> { - use Event::*; - match event { - WorkspaceChanged(id) => arm_async!(id, workspace_changed_events, self), - WorkspaceAdded(id) => arm_async!(id, workspace_added_events, self), - WorkspaceDeleted(data) => arm_async!(data, workspace_destroyed_events, self), - WorkspaceMoved(evend) => arm_async!(evend, workspace_moved_events, self), - WorkspaceRename(even) => arm_async!(even, workspace_rename_events, self), - ActiveMonitorChanged(evend) => arm_async!(evend, active_monitor_changed_events, self), - ActiveWindowChangedMerged(event) => { - arm_async!(event, active_window_changed_events, self) - } - ActiveWindowChangedV1(_) => (), - ActiveWindowChangedV2(_) => (), - FullscreenStateChanged(bool) => arm_async!(bool, fullscreen_state_changed_events, self), - MonitorAdded(monitor) => arm_async!(monitor, monitor_added_events, self), - MonitorRemoved(monitor) => arm_async!(monitor, monitor_removed_events, self), - WindowClosed(addr) => arm_async!(addr, window_close_events, self), - WindowMoved(even) => arm_async!(even, window_moved_events, self), - WindowOpened(even) => arm_async!(even, window_open_events, self), - SpecialRemoved(monitor) => arm_async!(monitor, special_removed_events, self), - ChangedSpecial(data) => arm_async!(data, special_changed_events, self), - LayoutChanged(even) => arm_async!(even, keyboard_layout_change_events, self), - SubMapChanged(map) => arm_async!(map, sub_map_changed_events, self), - LayerOpened(namespace) => arm_async!(namespace, layer_open_events, self), - LayerClosed(namespace) => arm_async!(namespace, layer_closed_events, self), - FloatStateChanged(even) => arm_async!(even, float_state_events, self), - UrgentStateChanged(even) => arm_async!(even, urgent_state_events, self), - Minimize(data) => arm_async!(data, minimize_events, self), - WindowTitleChanged(addr) => arm_async!(addr, window_title_changed_events, self), - Screencast(data) => arm_async!(data, screencast_events, self), - ConfigReloaded => arm_async!(config_reloaded_events, self), - IgnoreGroupLockStateChanged(bool) => { - arm_async!(bool, ignore_group_lock_state_changed_events, self) - } - LockGroupsStateChanged(bool) => { - arm_async!(bool, lock_groups_state_changed_events, self) - } - WindowPinned(data) => arm_async!(data, window_pin_state_toggled_events, self), - } - Ok(()) - } -} - impl AsyncEventListener { /// This method creates a new EventListener instance /// @@ -81,7 +34,7 @@ impl AsyncEventListener { /// ``` pub fn new() -> Self { Self { - events: init_events!(AsyncEvents), + events: create_events_async(), } } @@ -116,7 +69,8 @@ impl AsyncEventListener { let parsed: Vec = event_parser(string)?; for event in parsed { - self.event_primer_async(event, &mut active_windows).await?; + self.event_primer_exec_async(event, &mut active_windows) + .await?; } } diff --git a/src/event_listener/immutable.rs b/src/event_listener/immutable.rs index 9fe0fce..073fe45 100644 --- a/src/event_listener/immutable.rs +++ b/src/event_listener/immutable.rs @@ -25,47 +25,6 @@ impl Default for EventListener { } } -impl HasExecutor for EventListener { - fn event_executor(&mut self, event: Event) -> crate::Result<()> { - use Event::*; - match event { - WorkspaceChanged(id) => arm!(id, workspace_changed_events, self), - WorkspaceAdded(id) => arm!(id, workspace_added_events, self), - WorkspaceDeleted(data) => arm!(data, workspace_destroyed_events, self), - WorkspaceMoved(evend) => arm!(evend, workspace_moved_events, self), - WorkspaceRename(even) => arm!(even, workspace_rename_events, self), - ActiveMonitorChanged(evend) => arm!(evend, active_monitor_changed_events, self), - ActiveWindowChangedMerged(opt) => arm!(opt, active_window_changed_events, self), - ActiveWindowChangedV1(_) => (), - ActiveWindowChangedV2(_) => (), - FullscreenStateChanged(bool) => arm!(bool, fullscreen_state_changed_events, self), - MonitorAdded(monitor) => arm!(monitor, monitor_added_events, self), - MonitorRemoved(monitor) => arm!(monitor, monitor_removed_events, self), - WindowClosed(addr) => arm!(addr, window_close_events, self), - WindowMoved(even) => arm!(even, window_moved_events, self), - WindowOpened(even) => arm!(even, window_open_events, self), - SpecialRemoved(monitor) => arm!(monitor, special_removed_events, self), - ChangedSpecial(data) => arm!(data, special_changed_events, self), - LayoutChanged(even) => arm!(even, keyboard_layout_change_events, self), - SubMapChanged(map) => arm!(map, sub_map_changed_events, self), - LayerOpened(namespace) => arm!(namespace, layer_open_events, self), - LayerClosed(namespace) => arm!(namespace, layer_closed_events, self), - FloatStateChanged(even) => arm!(even, float_state_events, self), - UrgentStateChanged(even) => arm!(even, urgent_state_events, self), - Minimize(data) => arm!(data, minimize_events, self), - WindowTitleChanged(addr) => arm!(addr, window_title_changed_events, self), - Screencast(data) => arm!(data, screencast_events, self), - ConfigReloaded => arm!(config_reloaded_events, self), - IgnoreGroupLockStateChanged(bool) => { - arm!(bool, ignore_group_lock_state_changed_events, self) - } - LockGroupsStateChanged(bool) => arm!(bool, lock_groups_state_changed_events, self), - WindowPinned(data) => arm!(data, window_pin_state_toggled_events, self), - } - Ok(()) - } -} - impl EventListener { /// This method creates a new EventListener instance /// @@ -75,7 +34,7 @@ impl EventListener { /// ``` pub fn new() -> EventListener { EventListener { - events: init_events!(Events), + events: create_events(), } } diff --git a/src/event_listener/macros.rs b/src/event_listener/macros.rs index 89c4b57..1ef20db 100644 --- a/src/event_listener/macros.rs +++ b/src/event_listener/macros.rs @@ -1,12 +1,81 @@ -macro_rules! add_listener { - ($name:ident $end:ident,$f:ty,$c:literal,$c2:literal => $id:ident) => { - add_listener_reg!($name $end,$f,$c,$c2 => $id); - add_async_listener!($name $end,$f,$c,$c2 => $id); - }; - ($name:ident $end:ident,$c:literal,$c2:literal => $id:ident) => { - add_listener_reg!($name $end,$c,$c2 => $id); - add_async_listener!($name $end,$c,$c2 => $id); +macro_rules! events { + ($($name:ty => $f:ty,$c:literal,$c2:literal => $id:ident);*) => { + paste! { + pub(crate) struct Events { + $( + pub(crate) [<$name:snake _events>]: type_if! {(),$f,Vec, Closures<$f>} + ),* + } + #[allow(clippy::type_complexity)] + pub(crate) struct AsyncEvents { + $( + pub(crate) [<$name:snake _events>]: type_if! {(),$f,Vec, AsyncClosures<$f>} + ),* + } + pub(crate) fn create_events() -> Events { + Events { + $([<$name:snake _events>]: vec![]),* + } + } + pub(crate) fn create_events_async() -> AsyncEvents { + AsyncEvents { + $([<$name:snake _events>]: vec![]),* + } + } + + impl HasAsyncExecutor for AsyncEventListener { + async fn event_executor_async(&mut self, event: Event) -> crate::Result<()> { + use Event::*; + match event { + $( + expr_if! {(),$f, $name, $name($id)} => expr_if! { + (), + $f, + arm_async!([<$name:snake _events>], self), + arm_async!($id, [<$name:snake _events>], self) + }, + )* + _ => () + } + Ok(()) + } + } + impl HasExecutor for EventListener { + fn event_executor(&mut self, event: Event) -> crate::Result<()> { + use Event::*; + match event { + $( + expr_if! {(),$f, $name, $name($id)} => expr_if! { + (), + $f, + arm!([<$name:snake _events>], self), + arm!($id, [<$name:snake _events>], self) + }, + )* + _ => () + } + Ok(()) + } + } + } + $( + paste!{ + block_if!{ + (), + $f, + { + add_listener!{[<$name:snake>],$c,$c2 => $id} + }, + { + add_listener!{[<$name:snake>],$f,$c,$c2 => $id} + } + } + } + )* }; +} + +macro_rules! add_listener { ($name:ident,$f:ty,$c:literal,$c2:literal => $id:ident) => { add_listener_reg!($name,$f,$c,$c2 => $id); add_async_listener!($name,$f,$c,$c2 => $id); @@ -17,6 +86,23 @@ macro_rules! add_listener { }; } +macro_rules! add_async_listener { + ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + add_async_listener_raw!($name,$name,impl Fn($f) -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); + }; + ($name:ident,$c:literal,$c2:expr => $id:ident) => { + add_async_listener_raw!($name,$name,impl Fn() -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); + }; +} +macro_rules! add_listener_reg { + ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { + add_listener_reg_raw!($name,$name,impl Fn($f) + 'static,$c,$c2 => $id); + }; + ($name:ident,$c:literal,$c2:expr => $id:ident) => { + add_listener_reg_raw!($name,$name,impl Fn() + 'static,$c,$c2 => $id); + }; +} + macro_rules! add_listener_reg_raw { ($name:ident,$list_name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { @@ -34,26 +120,6 @@ listener.start_listener();"#)] } }; } - -macro_rules! add_listener_reg { - ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { - paste! { - add_listener_reg_raw!($name,[<$name $end>],impl Fn($f) + 'static,$c,$c2 => $id); - } - }; - ($name:ident $end:ident,$c:literal,$c2:expr => $id:ident) => { - paste! { - add_listener_reg_raw!($name,[<$name $end>],impl Fn() + 'static,$c,$c2 => $id); - } - }; - ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { - add_listener_reg_raw!($name,$name,impl Fn($f) + 'static,$c,$c2 => $id); - }; - ($name:ident,$c:literal,$c2:expr => $id:ident) => { - add_listener_reg_raw!($name,$name,impl Fn() + 'static,$c,$c2 => $id); - }; -} - macro_rules! add_async_listener_raw { ($name:ident,$list_name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { paste! { @@ -72,25 +138,6 @@ listener.start_listener();"#)] }; } -macro_rules! add_async_listener { - ($name:ident $end:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { - paste! { - add_async_listener_raw!($name,[<$name $end>],impl Fn($f) -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); - } - }; - ($name:ident $end:ident,$c:literal,$c2:expr => $id:ident) => { - paste! { - add_async_listener_raw!($name,[<$name $end>],impl Fn() -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); - } - }; - ($name:ident,$f:ty,$c:literal,$c2:expr => $id:ident) => { - add_async_listener_raw!($name,$name,impl Fn($f) -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); - }; - ($name:ident,$c:literal,$c2:expr => $id:ident) => { - add_async_listener_raw!($name,$name,impl Fn() -> VoidFuture + Send + Sync + 'static,$c,$c2 => $id); - }; -} - macro_rules! arm { ($val:expr,$nam:ident,$se:ident) => {{ let events = &$se.events.$nam; @@ -120,38 +167,3 @@ macro_rules! arm_async { } }}; } - -macro_rules! init_events { - ($name:ident) => { - $name { - workspace_changed_events: vec![], - workspace_added_events: vec![], - workspace_destroyed_events: vec![], - workspace_moved_events: vec![], - workspace_rename_events: vec![], - active_monitor_changed_events: vec![], - active_window_changed_events: vec![], - fullscreen_state_changed_events: vec![], - monitor_removed_events: vec![], - monitor_added_events: vec![], - window_open_events: vec![], - window_close_events: vec![], - window_moved_events: vec![], - special_removed_events: vec![], - special_changed_events: vec![], - keyboard_layout_change_events: vec![], - sub_map_changed_events: vec![], - layer_open_events: vec![], - layer_closed_events: vec![], - float_state_events: vec![], - urgent_state_events: vec![], - minimize_events: vec![], - window_title_changed_events: vec![], - screencast_events: vec![], - config_reloaded_events: vec![], - ignore_group_lock_state_changed_events: vec![], - lock_groups_state_changed_events: vec![], - window_pin_state_toggled_events: vec![], - } - }; -} diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index 9cd616b..e7fd086 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -1,3 +1,9 @@ +//! # Event Listener Module +//! for documentation go to: +//! * [crate::event_listener::EventStream] for the event listener implementation based on the [futures_lite::Stream] api +//! * [crate::event_listener::EventListener] for the normal [Fn] based event listener +//! * [crate::event_listener::AsyncEventListener] for the [Fn] based event listener which uses closures that return [std::future::Future]s + #[macro_use] mod macros; @@ -12,31 +18,41 @@ pub use crate::event_listener::immutable::EventListener; mod async_im; pub use crate::event_listener::async_im::AsyncEventListener; -add_listener!(workspace_change d, WorkspaceType, "on workspace change", "changed workspace to" => id); -add_listener!(workspace_added, WorkspaceType, "a workspace is created", "workspace was added" => id); -add_listener!(workspace_destroy ed, WorkspaceDestroyedEventData, "a workspace is destroyed", "a workspace was destroyed" => data); -add_listener!(workspace_moved, MonitorEventData, "a workspace is moved", "workspace was moved" => id); -add_listener!(workspace_rename, WorkspaceRenameEventData, "a workspace is renamed", "workspace was renamed" => id); -add_listener!(active_monitor_change d, MonitorEventData, "the active monitor is changed", "Active monitor changed to" => data); -add_listener!(active_window_change d, Option, "the active window is changed", "Active window changed" => data); -add_listener!(fullscreen_state_change d, bool, "the fullscreen state is changed", "Fullscreen is on" => state); -add_listener!(monitor_added, String, "a new monitor is added", "Monitor added" => data); -add_listener!(monitor_removed, String, "a monitor is removed", "Monitor removed" => data); -add_listener!(window_open, WindowOpenEvent, "a window is opened", "Window opened" => data); -add_listener!(window_close, Address, "a window is closed", "Window closed" => data); -add_listener!(window_moved, WindowMoveEvent, "a window is moved", "Window moved" => data); -add_listener!(special_remove d, String, "a monitor's special workspace is removed", "Special Workspace removed" => monitor); -add_listener!(special_change d, MonitorEventData, "a monitor's special workspace is changed", "Special Workspace changed" => data); -add_listener!(keyboard_layout_change, LayoutEvent, "the keyboard layout is changed", "Layout changed" => data); -add_listener!(sub_map_change d, String, "the submap is changed", "Submap changed" => data); -add_listener!(layer_open, String, "a new layer is opened", "Layer opened" => data); -add_listener!(layer_closed, String, "a layer is closed", "Layer closed" => data); -add_listener!(float_state, WindowFloatEventData, "the float state of a window is changed", "Float state changed" => data); -add_listener!(urgent_state, Address, "the urgent state of a window is changed", "urgent state changed" => data); -add_listener!(minimize, MinimizeEventData, "the minimize state of a window is changed", "minimize state changed" => data); -add_listener!(window_title_change d, Address, "a window title is changed", "A window title changed" => data); -add_listener!(screencast, ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data); -add_listener!(config_reload ed, "the configuration of Hyprland is reloaded", "config reloaded" => _empty); -add_listener!(ignore_group_lock_state_change d, bool, "the state of ignore group lock is toggled", "ignore group lock toggled to" => data); -add_listener!(lock_groups_state_change d, bool, "the state of lock groups is toggled", "lock group state toggled to" => data); -add_listener!(window_pin_state_toggle d, WindowPinEventData, "the pinned state of a window is changed", "window pin was set to" => state); +mod stream; +pub use crate::event_listener::stream::EventStream; + +// generates code for the closure based event listeners +events! { + WorkspaceChanged => WorkspaceEventData, "on workspace change", "changed workspace to" => id; + WorkspaceAdded => WorkspaceEventData, "a workspace is created", "workspace was added" => id; + WorkspaceDeleted => WorkspaceEventData, "a workspace is destroyed", "a workspace was destroyed" => data; + WorkspaceMoved => WorkspaceMovedEventData, "a workspace is moved", "workspace was moved" => id; + WorkspaceRenamed => NonSpecialWorkspaceEventData, "a workspace is renamed", "workspace was renamed" => id; + ActiveMonitorChanged => MonitorEventData, "the active monitor is changed", "Active monitor changed to" => data; + ActiveWindowChanged => Option, "the active window is changed", "Active window changed" => data; + FullscreenStateChanged => bool, "the fullscreen state is changed", "Fullscreen is on" => state; + MonitorAdded => MonitorAddedEventData, "a new monitor is added", "Monitor added" => data; + MonitorRemoved => String, "a monitor is removed", "Monitor removed" => data; + WindowOpened => WindowOpenEvent, "a window is opened", "Window opened" => data; + WindowClosed => Address, "a window is closed", "Window closed" => data; + WindowMoved => WindowMoveEvent, "a window is moved", "Window moved" => data; + SpecialRemoved => String, "a monitor's special workspace is removed", "Special Workspace removed" => monitor; + ChangedSpecial => ChangedSpecialEventData, "a monitor's special workspace is changed", "Special Workspace changed" => data; + LayoutChanged => LayoutEvent, "the keyboard layout is changed", "Layout changed" => data; + SubMapChanged => String, "the submap is changed", "Submap changed" => data; + LayerOpened => String, "a new layer is opened", "Layer opened" => data; + LayerClosed => String, "a layer is closed", "Layer closed" => data; + FloatStateChanged => WindowFloatEventData, "the float state of a window is changed", "Float state changed" => data; + UrgentStateChanged => Address, "the urgent state of a window is changed", "urgent state changed" => data; + Minimize => MinimizeEventData, "the minimize state of a window is changed", "minimize state changed" => data; + WindowTitleChanged => WindowTitleEventData, "a window title is changed", "A window title changed" => data; + Screencast => ScreencastEventData, "the screencast state of a window is changed", "screencast state changed" => data; + ConfigReloaded => (), "the configuration of Hyprland is reloaded", "config reloaded" => _empty; + IgnoreGroupLockStateChanged => bool, "the state of ignore group lock is toggled", "ignore group lock toggled to" => data; + LockGroupsStateChanged => bool, "the state of lock groups is toggled", "lock group state toggled to" => data; + WindowPinned => WindowPinEventData, "the pinned state of a window is changed", "window pin was set to" => state; + GroupToggled => GroupToggledEventData, "a group was toggled", "the group toggle state was set to" => data; + WindowMovedIntoGroup => Address, "a window was moved into a group", "a window was moved into a group with the address of" => addr; + WindowMovedOutOfGroup => Address, "a window was moved out of a group", "a window was moved out of a group with the address of" => addr; + Unknown => UnknownEventData, "the state of some unknown event changed", "unknown state changed to" => value +} diff --git a/src/event_listener/shared.rs b/src/event_listener/shared.rs index 99c1719..a742cba 100644 --- a/src/event_listener/shared.rs +++ b/src/event_listener/shared.rs @@ -1,6 +1,4 @@ use crate::shared::*; -use once_cell::sync::Lazy; -use regex::{Error as RegexError, Regex}; use std::{fmt::Debug, pin::Pin}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -74,10 +72,59 @@ pub(crate) trait HasExecutor { } } +pub(crate) fn event_primer_noexec<'a>( + event: Event, + abuf: &mut Vec, +) -> crate::Result> { + if abuf.is_empty() { + abuf.push(ActiveWindowState::new()); + } + let mut events: Vec = vec![]; + if let Event::ActiveWindowChangedV1(data) = event { + let mut to_remove = vec![]; + let data = into(data); + for (index, awin) in abuf.iter_mut().enumerate() { + if awin.title.is_empty() && awin.class.is_empty() { + (awin.class, awin.title) = data.clone(); + } + if awin.ready() { + if let Some(event) = awin.get_event() { + events.push(event); + }; + to_remove.push(index); + break; + } + } + for index in to_remove.into_iter().rev() { + abuf.swap_remove(index); + } + } else if let Event::ActiveWindowChangedV2(data) = event { + let mut to_remove = vec![]; + for (index, awin) in abuf.iter_mut().enumerate() { + if awin.addr.is_empty() { + awin.addr = data.clone().into(); + } + if awin.ready() { + if let Some(event) = awin.get_event() { + events.push(event); + }; + to_remove.push(index); + break; + } + } + for index in to_remove.into_iter().rev() { + abuf.swap_remove(index); + } + } else { + events.push(event); + } + Ok(events) +} + pub(crate) trait HasAsyncExecutor { async fn event_executor_async(&mut self, event: Event) -> crate::Result<()>; - async fn event_primer_async( + async fn event_primer_exec_async( &mut self, event: Event, abuf: &mut Vec, @@ -85,42 +132,8 @@ pub(crate) trait HasAsyncExecutor { where Self: std::marker::Sized, { - if abuf.is_empty() { - abuf.push(ActiveWindowState::new()); - } - if let Event::ActiveWindowChangedV1(data) = event { - let mut to_remove = vec![]; - let data = into(data); - for (index, awin) in abuf.iter_mut().enumerate() { - if awin.title.is_empty() && awin.class.is_empty() { - (awin.class, awin.title) = data.clone(); - } - if awin.ready() { - awin.execute_async(self).await?; - to_remove.push(index); - break; - } - } - for index in to_remove.into_iter().rev() { - abuf.swap_remove(index); - } - } else if let Event::ActiveWindowChangedV2(data) = event { - let mut to_remove = vec![]; - for (index, awin) in abuf.iter_mut().enumerate() { - if awin.addr.is_empty() { - awin.addr = data.clone().into(); - } - if awin.ready() { - awin.execute_async(self).await?; - to_remove.push(index); - break; - } - } - for index in to_remove.into_iter().rev() { - abuf.swap_remove(index); - } - } else { - self.event_executor_async(event).await?; + for x in event_primer_noexec(event, abuf)? { + self.event_executor_async(x).await?; } Ok(()) } @@ -131,38 +144,32 @@ impl ActiveWindowState { use ActiveWindowValue::{None, Queued}; let data = (&self.title, &self.class, &self.addr); if let (Queued(ref title), Queued(ref class), Queued(ref addr)) = data { - listener.event_executor(Event::ActiveWindowChangedMerged(Some(WindowEventData { - window_class: class.to_string(), - window_title: title.to_string(), - window_address: addr.clone(), + listener.event_executor(Event::ActiveWindowChanged(Some(WindowEventData { + class: class.to_string(), + title: title.to_string(), + address: addr.clone(), })))?; self.reset(); } else if let (None, None, None) = data { - listener.event_executor(Event::ActiveWindowChangedMerged(Option::None))?; + listener.event_executor(Event::ActiveWindowChanged(Option::None))?; } Ok(()) } - pub async fn execute_async( - &mut self, - listener: &mut T, - ) -> crate::Result<()> { + pub fn get_event(&mut self) -> Option { use ActiveWindowValue::{None, Queued}; let data = (&self.title, &self.class, &self.addr); + let mut event = Option::None; if let (Queued(ref title), Queued(ref class), Queued(ref addr)) = data { - listener - .event_executor_async(Event::ActiveWindowChangedMerged(Some(WindowEventData { - window_class: class.to_string(), - window_title: title.to_string(), - window_address: addr.clone(), - }))) - .await?; + event = Some(Event::ActiveWindowChanged(Some(WindowEventData { + class: class.to_string(), + title: title.to_string(), + address: addr.clone(), + }))); self.reset(); } else if let (None, None, None) = data { - listener - .event_executor_async(Event::ActiveWindowChangedMerged(Option::None)) - .await?; + event = Some(Event::ActiveWindowChanged(Option::None)); } - Ok(()) + event } pub fn ready(&self) -> bool { @@ -214,103 +221,22 @@ pub(crate) type EmptyAsyncClosure = AsyncEventType Voi pub(crate) type Closures = Vec>; pub(crate) type AsyncClosures = Vec>; -pub(crate) struct Events { - pub(crate) workspace_changed_events: Closures, - pub(crate) workspace_added_events: Closures, - pub(crate) workspace_destroyed_events: Closures, - pub(crate) workspace_moved_events: Closures, - pub(crate) workspace_rename_events: Closures, - pub(crate) active_monitor_changed_events: Closures, - pub(crate) active_window_changed_events: Closures>, - pub(crate) fullscreen_state_changed_events: Closures, - pub(crate) monitor_removed_events: Closures, - pub(crate) monitor_added_events: Closures, - pub(crate) special_removed_events: Closures, - pub(crate) special_changed_events: Closures, - pub(crate) keyboard_layout_change_events: Closures, - pub(crate) sub_map_changed_events: Closures, - pub(crate) window_open_events: Closures, - pub(crate) window_close_events: Closures
, - pub(crate) window_moved_events: Closures, - pub(crate) layer_open_events: Closures, - pub(crate) layer_closed_events: Closures, - pub(crate) float_state_events: Closures, - pub(crate) urgent_state_events: Closures
, - pub(crate) minimize_events: Closures, - pub(crate) window_title_changed_events: Closures
, - pub(crate) screencast_events: Closures, - pub(crate) config_reloaded_events: Vec, - pub(crate) ignore_group_lock_state_changed_events: Closures, - pub(crate) lock_groups_state_changed_events: Closures, - pub(crate) window_pin_state_toggled_events: Closures, -} - -#[allow(clippy::type_complexity)] -pub(crate) struct AsyncEvents { - pub(crate) workspace_changed_events: AsyncClosures, - pub(crate) workspace_added_events: AsyncClosures, - pub(crate) workspace_destroyed_events: AsyncClosures, - pub(crate) workspace_moved_events: AsyncClosures, - pub(crate) workspace_rename_events: AsyncClosures, - pub(crate) active_monitor_changed_events: AsyncClosures, - pub(crate) active_window_changed_events: AsyncClosures>, - pub(crate) fullscreen_state_changed_events: AsyncClosures, - pub(crate) monitor_removed_events: AsyncClosures, - pub(crate) monitor_added_events: AsyncClosures, - pub(crate) special_removed_events: AsyncClosures, - pub(crate) special_changed_events: AsyncClosures, - pub(crate) keyboard_layout_change_events: AsyncClosures, - pub(crate) sub_map_changed_events: AsyncClosures, - pub(crate) window_open_events: AsyncClosures, - pub(crate) window_close_events: AsyncClosures
, - pub(crate) window_moved_events: AsyncClosures, - pub(crate) layer_open_events: AsyncClosures, - pub(crate) layer_closed_events: AsyncClosures, - pub(crate) float_state_events: AsyncClosures, - pub(crate) urgent_state_events: AsyncClosures
, - pub(crate) minimize_events: AsyncClosures, - pub(crate) window_title_changed_events: AsyncClosures
, - pub(crate) screencast_events: AsyncClosures, - pub(crate) config_reloaded_events: Vec, - pub(crate) ignore_group_lock_state_changed_events: AsyncClosures, - pub(crate) lock_groups_state_changed_events: AsyncClosures, - pub(crate) window_pin_state_toggled_events: AsyncClosures, -} - -/// Event data for destroyworkspacev2 event -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct WorkspaceDestroyedEventData { - /// Workspace Id - pub workspace_id: WorkspaceId, - /// Workspace name - pub workspace_name: String, -} - -/// Event data for renameworkspace event -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct WorkspaceRenameEventData { - /// Workspace id - pub workspace_id: WorkspaceId, - /// Workspace name content - pub workspace_name: String, -} - /// Event data for a minimize event #[derive(Clone, Debug, PartialEq, Eq)] pub struct MinimizeEventData { /// Window address pub window_address: Address, /// whether it's minimized or not - pub is_minimized: bool, + pub minimized: bool, } /// Event data for screencast event #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ScreencastEventData { /// State/Is it turning on? - pub is_turning_on: bool, + pub turning_on: bool, /// Owner type, is it a monitor? - pub is_monitor: bool, + pub monitor: bool, } /// The data for the event executed when moving a window to a new workspace @@ -318,12 +244,14 @@ pub struct ScreencastEventData { pub struct WindowMoveEvent { /// Window address pub window_address: Address, + /// the workspace id + pub workspace_id: WorkspaceId, /// The workspace name - pub workspace_name: String, + pub workspace_name: WorkspaceType, } /// The data for the event executed when opening a new window -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct WindowOpenEvent { /// Window address pub window_address: Address, @@ -436,15 +364,45 @@ pub(crate) async fn execute_closure_async(f: &AsyncClosure, val: T) { f(val).await; } +/// This struct holds workspace event data +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WorkspaceEventData { + /// The workspace name + pub name: WorkspaceType, + /// The window id + pub id: WorkspaceId, +} + +/// This struct holds workspace event data +/// when the workspace cannot be special +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NonSpecialWorkspaceEventData { + /// The workspace name + pub name: String, + /// The window id + pub id: WorkspaceId, +} + +/// This struct holds workspace moved event data +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WorkspaceMovedEventData { + /// The workspace name + pub name: WorkspaceType, + /// The window id + pub id: WorkspaceId, + /// The monitor name + pub monitor: String, +} + /// This struct holds window event data #[derive(Debug, Clone, PartialEq, Eq)] pub struct WindowEventData { /// The window class - pub window_class: String, + pub class: String, /// The window title - pub window_title: String, + pub title: String, /// The window address - pub window_address: Address, + pub address: Address, } /// This struct holds monitor event data @@ -452,61 +410,191 @@ pub struct WindowEventData { pub struct MonitorEventData { /// The monitor name pub monitor_name: String, - /// The workspace - pub workspace: WorkspaceType, + /// The workspace name + pub workspace_name: Option, +} + +/// This struct holds changed special event data +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ChangedSpecialEventData { + /// The monitor name + pub monitor_name: String, + /// The workspace name + pub workspace_name: String, +} + +/// This struct holds monitor event data +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MonitorAddedEventData { + /// The monitor's id + pub id: u8, + /// The monitor's name + pub name: String, + /// the monitor's description + pub description: String, } /// This struct holds window float event data #[derive(Debug, Clone, PartialEq, Eq)] pub struct WindowFloatEventData { /// The window address - pub window_address: Address, + pub address: Address, /// The float state - pub is_floating: bool, + pub floating: bool, } /// This struct holds window pin event data #[derive(Debug, Clone, PartialEq, Eq)] pub struct WindowPinEventData { /// The window address - pub window_address: Address, + pub address: Address, /// The pin state - pub is_pinned: bool, + pub pinned: bool, +} + +/// This struct holds the event data for the windowtitle changed event +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WindowTitleEventData { + /// The window address + pub address: Address, + /// The window title + pub title: String, +} + +/// This struct represents an unknown event to hyprland-rs +/// this allows you to use events that haven't been implemented in hyprland-rs. +/// To use this use the [UnknownEventData::parse_args] method to properly get the args +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnknownEventData { + /// The event's name + pub name: String, + /// The args as a string + pub args: String, +} + +impl UnknownEventData { + /// Takes the amount of args, and splits the string correctly + pub fn parse_args(self, count: usize) -> Vec { + self.args + .splitn(count, ",") + .map(|x| x.to_string()) + .collect() + } +} +/// This struct holds the data for the [Event::GroupToggled] event +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GroupToggledEventData { + /// The toggle status, `false` means the group was destroyed + pub toggled: bool, + /// The window addresses associated with the group + pub window_addresses: Vec
, } /// This enum holds every event type #[derive(Debug, Clone)] -pub(crate) enum Event { - WorkspaceChanged(WorkspaceType), - WorkspaceDeleted(WorkspaceDestroyedEventData), - WorkspaceAdded(WorkspaceType), - WorkspaceMoved(MonitorEventData), - WorkspaceRename(WorkspaceRenameEventData), - ActiveWindowChangedV1(Option<(String, String)>), - ActiveWindowChangedV2(Option
), - ActiveWindowChangedMerged(Option), +pub enum Event { + /// An unknown event + Unknown(UnknownEventData), + /// An event that emits when the current workspace is changed, + /// it is the equivelant of the `workspacev2` event + WorkspaceChanged(WorkspaceEventData), + /// An event that emits when a workspace is deleted, + /// it is the equivelant of the `destroyworkspacev2` event + WorkspaceDeleted(WorkspaceEventData), + /// An event that emits when a workspace is created, + /// it is the equivelant of the `createworkspacev2` event + WorkspaceAdded(WorkspaceEventData), + /// An event that emits when a workspace is moved to another monitor, + /// it is the equivelant of the `moveworkspacev2` event + WorkspaceMoved(WorkspaceMovedEventData), + /// An event that emits when a workspace is renamed, + /// it is the equivelant of the `renameworkspace` event + WorkspaceRenamed(NonSpecialWorkspaceEventData), + #[doc(hidden)] + ActiveWindowChangedV1(Option<(String, String)>), // internal intermediary event + #[doc(hidden)] + ActiveWindowChangedV2(Option
), // internal intermediary event + /// An event that emits when the active window is changed + /// Unlike the other events, this is a combination of 2 events + /// Those being `activewindow` and `activewindowv2`, + /// it waits for both, and then sends one unified event :) + ActiveWindowChanged(Option), + /// An event that emits when the active monitor is changed, + /// it is the equivelant of the `focusedmon` event ActiveMonitorChanged(MonitorEventData), + /// An event that emits when the current fullscreen state is changed, + /// it is the equivelant of the `fullscreen` event FullscreenStateChanged(bool), - MonitorAdded(String), + /// An event that emits when a new monitor is added/connected, + /// it is the equivelant of the `monitoraddedv2` event + MonitorAdded(MonitorAddedEventData), + /// An event that emits when a monitor is removed/disconnected, + /// it is the equivelant of the `monitorremoved` event MonitorRemoved(String), + /// An event that emits when a window is opened, + /// it is the equivelant of the `openwindow` event WindowOpened(WindowOpenEvent), + /// An event that emits when a window is closed, + /// it is the equivelant of the `closewindow` event WindowClosed(Address), + /// An event that emits when a window is moved to a different workspace, + /// it is the equivelant of the `movewindowv2` event WindowMoved(WindowMoveEvent), + /// An event that emits when a special workspace is closed on the current monitor, + /// it is the equivelant of the `activespecial` event SpecialRemoved(String), - ChangedSpecial(MonitorEventData), + /// An event that emits when the current special workspace is changed on a monitor, + /// it is the equivelant of the `activespecial` event + ChangedSpecial(ChangedSpecialEventData), + /// An event that emits when the layout of a keyboard changes, + /// it is the equivelant of the `activelayout` event LayoutChanged(LayoutEvent), + /// An event that emits when the current keybind submap changes, + /// it is the equivelant of the `submap` event SubMapChanged(String), + /// An event that emits when a layer shell surface is opened/mapped, + /// it is the equivelant of the `openlayer` event LayerOpened(String), + /// An event that emits when a layer shell surface is closed/unmapped, + /// it is the equivelant of the `closelayer` event LayerClosed(String), + /// An event that emits when the floating state of a window changes, + /// it is the equivelant of the `changefloatingmode` event FloatStateChanged(WindowFloatEventData), + /// An event that emits when the a window requests the urgent state, + /// it is the equivelant of the `urgent` event UrgentStateChanged(Address), + /// An event that emits when a window requests to be minimized, + /// it is the equivelant of the `minimize` event Minimize(MinimizeEventData), - WindowTitleChanged(Address), + /// An event that emits when the title of a window changes, + /// it is the equivelant of the `windowtitlev2` event + WindowTitleChanged(WindowTitleEventData), + /// An event that emits when the screencopy state of a client changes + /// AKA, a process wants to capture/record your screen, + /// it is the equivelant of the `screencast` event Screencast(ScreencastEventData), + /// An event that emits when hyprland is reloaded, + /// it is the equivelant of the `configreloaded` event ConfigReloaded, + /// An event that emits when `ignoregrouplock` is toggled, + /// it is the equivelant of the `ignoregrouplock` event IgnoreGroupLockStateChanged(bool), + /// An event that emits when `lockgroups` is toggled, + /// it is the equivelant of the `lockgroups` event LockGroupsStateChanged(bool), - WindowPinned(WindowPinEventData) + /// An event that emits when a window is pinned or unpinned, + /// it is the equivelant of the `pin` event + WindowPinned(WindowPinEventData), + /// And event that emits when a group is toggled, + /// it is the equivelant of the `togglegroup` + GroupToggled(GroupToggledEventData), + /// And event that emits when a window is moved into a group, + /// it is the equivelant of the `moveintogroup` + WindowMovedIntoGroup(Address), + /// And event that emits when a window is moved out of a group, + /// it is the equivelant of the `moveoutofgroup` + WindowMovedOutOfGroup(Address), } fn parse_string_as_work(str: String) -> WorkspaceType { @@ -526,46 +614,22 @@ fn parse_string_as_work(str: String) -> WorkspaceType { } } -macro_rules! report_unknown { - ($event:expr) => { - #[cfg(not(feature = "silent"))] - eprintln!( - "An unknown event was passed into Hyprland-rs - PLEASE MAKE AN ISSUE!! - The event was: {event}", - event = $event - ); - }; -} - -#[cfg(feature = "ahash")] -use ahash::{HashSet, HashSetExt}; -#[cfg(not(feature = "ahash"))] -use std::collections::HashSet; - -#[cfg(feature = "parking_lot")] -use parking_lot::Mutex; -#[cfg(not(feature = "parking_lot"))] -use std::sync::Mutex; - -static CHECK_TABLE: Lazy>> = Lazy::new(|| Mutex::new(HashSet::new())); - #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] -enum ParsedEventType { - WorkspaceChanged, +pub(crate) enum ParsedEventType { + WorkspaceChangedV2, WorkspaceDeletedV2, - WorkspaceAdded, - WorkspaceMoved, + WorkspaceAddedV2, + WorkspaceMovedV2, WorkspaceRename, ActiveWindowChangedV1, ActiveWindowChangedV2, ActiveMonitorChanged, FullscreenStateChanged, - MonitorAdded, + MonitorAddedV2, MonitorRemoved, WindowOpened, WindowClosed, - WindowMoved, + WindowMovedV2, ActiveSpecial, LayoutChanged, SubMapChanged, @@ -574,218 +638,167 @@ enum ParsedEventType { FloatStateChanged, UrgentStateChanged, Minimize, - WindowTitleChanged, + WindowTitleChangedV2, Screencast, ConfigReloaded, IgnoreGroupLock, LockGroups, Pin, - Unknown, -} - -/// All the recognized events -static EVENT_SET: Lazy> = Lazy::new(|| { - [ - ( - ParsedEventType::WorkspaceChanged, - r"\bworkspace>>(?P.*)", - ), - ( - ParsedEventType::WorkspaceDeletedV2, - r"destroyworkspacev2>>(?P.*),(?P.*)", - ), - ( - ParsedEventType::WorkspaceAdded, - r"createworkspace>>(?P.*)", - ), - ( - ParsedEventType::WorkspaceMoved, - r"moveworkspace>>(?P.*),(?P.*)", - ), - ( - ParsedEventType::WorkspaceRename, - r"renameworkspace>>(?P.*),(?P.*)", - ), - ( - ParsedEventType::ActiveMonitorChanged, - r"focusedmon>>(?P.*),(?P.*)", - ), - ( - ParsedEventType::ActiveWindowChangedV1, - r"activewindow>>(?P.*?),(?P.*)", - ), - ( - ParsedEventType::ActiveWindowChangedV2, - r"activewindowv2>>(?P<address>.*)", - ), - ( - ParsedEventType::FullscreenStateChanged, - r"fullscreen>>(?P<state>0|1)", - ), - ( - ParsedEventType::MonitorRemoved, - r"monitorremoved>>(?P<monitor>.*)", - ), - ( - ParsedEventType::MonitorAdded, - r"monitoradded>>(?P<monitor>.*)", - ), - ( - ParsedEventType::WindowOpened, - r"openwindow>>(?P<address>.*),(?P<workspace>.*),(?P<class>.*),(?P<title>.*)", - ), - ( - ParsedEventType::WindowClosed, - r"closewindow>>(?P<address>.*)", - ), - ( - ParsedEventType::WindowMoved, - r"movewindow>>(?P<address>.*),(?P<workspace>.*)", - ), - ( - ParsedEventType::LayoutChanged, - r"activelayout>>(?P<keyboard>.*)(?P<layout>.*)", - ), - ( - ParsedEventType::ActiveSpecial, - r"activespecial>>(?P<workspace>.*),(?P<monitor>.*)", - ), - (ParsedEventType::SubMapChanged, r"submap>>(?P<submap>.*)"), - ( - ParsedEventType::LayerOpened, - r"openlayer>>(?P<namespace>.*)", - ), - ( - ParsedEventType::LayerClosed, - r"closelayer>>(?P<namespace>.*)", - ), - ( - ParsedEventType::FloatStateChanged, - r"changefloatingmode>>(?P<address>.*),(?P<floatstate>[0-1])", - ), - ( - ParsedEventType::Minimize, - r"minimize>>(?P<address>.*),(?P<state>[0-1])", - ), - ( - ParsedEventType::Screencast, - r"screencast>>(?P<state>[0-1]),(?P<owner>[0-1])", - ), - ( - ParsedEventType::UrgentStateChanged, - r"urgent>>(?P<address>.*)", - ), - ( - ParsedEventType::WindowTitleChanged, - r"windowtitle>>(?P<address>.*)", - ), - (ParsedEventType::ConfigReloaded, r"configreloaded>>"), - (ParsedEventType::IgnoreGroupLock, r"ignoregrouplock>>(?P<state>[0-1])"), - (ParsedEventType::LockGroups, r"lockgroups>>(?P<state>[0-1])"), - (ParsedEventType::Pin, r"pin>>(?P<address>.*),(?P<state>[0-1])"), - (ParsedEventType::Unknown, r"(?P<Event>^[^>]*)"), - ].into_iter() - .map(|(e, r)| ( - e, - match Regex::new(r) { - Ok(value) => value, - Err(e) => { - // I believe that panics here are fine because the chances of the library user finding them are extremely high - // This check does occur at runtime though... - eprintln!("An internal error occured in hyprland-rs while parsing regex! Please open an issue!"); - match e { - RegexError::Syntax(str) => panic!("Regex syntax error: {str}"), - RegexError::CompiledTooBig(size) => { - panic!("The compiled regex size is too big! ({size})") - } - _ => panic!("Error compiling regex: {e}"), - } + ToggleGroup, + MoveIntoGroup, + MoveOutOfGroup, +} + +/// All Hyprland events's arg count and enum variant. +/// The first item of the tuple is a usize of the argument count +/// This allows for easy parsing because the last arg in a Hyprland event +/// has the ability to have extra `,`s +pub(crate) static EVENTS: phf::Map<&'static str, (usize, ParsedEventType)> = phf::phf_map! { + "workspacev2" => ((2),ParsedEventType::WorkspaceChangedV2), + "destroyworkspacev2" => ((2),ParsedEventType::WorkspaceDeletedV2), + "createworkspacev2" => ((2),ParsedEventType::WorkspaceAddedV2), + "moveworkspacev2" => ((3),ParsedEventType::WorkspaceMovedV2), + "renameworkspace" => ((2),ParsedEventType::WorkspaceRename), + "focusedmon" => ((2),ParsedEventType::ActiveMonitorChanged), + "activewindow" => ((2),ParsedEventType::ActiveWindowChangedV1), + "activewindowv2" => ((1),ParsedEventType::ActiveWindowChangedV2), + "fullscreen" => ((1),ParsedEventType::FullscreenStateChanged), + "monitorremoved" => ((1),ParsedEventType::MonitorRemoved), + "monitoraddedv2" => ((3),ParsedEventType::MonitorAddedV2), + "openwindow" => ((4),ParsedEventType::WindowOpened), + "closewindow" => ((1),ParsedEventType::WindowClosed), + "movewindow" => ((3),ParsedEventType::WindowMovedV2), + "activelayout" => ((2),ParsedEventType::LayoutChanged), + "activespecial" => ((2),ParsedEventType::ActiveSpecial), + "submap" => ((1), ParsedEventType::SubMapChanged), + "openlayer" => ((1),ParsedEventType::LayerOpened), + "closelayer" => ((1),ParsedEventType::LayerClosed), + "changefloatingmode" => ((2),ParsedEventType::FloatStateChanged), + "minimize" => ((2),ParsedEventType::Minimize), + "screencast" => ((2),ParsedEventType::Screencast), + "urgent" => ((1),ParsedEventType::UrgentStateChanged), + "windowtitlev2" => ((1),ParsedEventType::WindowTitleChangedV2), + "configreloaded" => ((0),ParsedEventType::ConfigReloaded), + "ignoregrouplock" => ((1),ParsedEventType::IgnoreGroupLock), + "lockgroups" => ((1),ParsedEventType::LockGroups), + "pin" => ((2),ParsedEventType::Pin), + "togglegroup" => (2,ParsedEventType::ToggleGroup), + "moveintogroup" => (1,ParsedEventType::MoveIntoGroup), + "moveoutofgroup" => (1,ParsedEventType::MoveOutOfGroup) +}; + +use either::Either; + +fn new_event_parser( + input: &str, +) -> crate::Result<Either<(ParsedEventType, Vec<String>), (String, String)>> { + input + .to_string() + .split_once(">>") + .ok_or(HyprError::Other( + "could not get event name from Hyprland IPC data (not hyprland-rs)".to_string(), + )) + .map(|(name, x)| { + if let Some(event) = EVENTS.get(name) { + Either::Left(( + event.1, + x.splitn(event.0 as usize, ",") + .map(|y| y.to_string()) + .collect(), + )) + } else { + Either::Right((name.to_string(), x.to_string())) } }) - ).collect() -}); +} + +macro_rules! parse_int { + ($int:expr, event: $event:literal) => { + parse_int!($int, event: $event => WorkspaceId) + }; + ($int:expr, event: $event:literal => $int_type:ty) => { + ($int + .parse::<$int_type>() + .map_err(|e| + HyprError::Internal(format!(concat!($event, ": invalid integer error: {}"), e)) + )? + ) + }; + +} + +macro_rules! get { + ($args:expr ; $id:literal) => { + get![ref $args;$id].clone() + }; + (ref $args:expr ; $id:literal) => { + $args + .get($id) + .ok_or(HyprError::Internal( + concat!("could not get the event arg of index ", stringify!($id)).to_string(), + ))? + }; +} /// This internal function parses event strings pub(crate) fn event_parser(event: String) -> crate::Result<Vec<Event>> { // TODO: Optimize nested looped regex capturing. Maybe pull in rayon if possible. - let event_iter = event - .trim() - .lines() - .map(|event_line| { - let type_matches = EVENT_SET - .iter() - .filter_map(|(event_type, regex)| Some((event_type, regex.captures(event_line)?))) - .collect::<Vec<_>>(); - (event_line, type_matches) - }) - .filter(|(_, b)| !b.is_empty()); - - let mut temp_event_holder = Vec::new(); - - for (event_str, matches) in event_iter { - match matches.len() { - 0 => hypr_err!( - "A Hyprland event that has no regex matches was passed! Please file a bug report!" - ), - 1 => { - report_unknown!((event_str.split('>').next().unwrap_or("unknown"))); - continue; - } - 2 => { - let (event_type, captures) = match matches - .into_iter() - .find(|(e, _)| **e != ParsedEventType::Unknown) { - Some(t) => t, - None => hypr_err!("The only events captured were unknown Hyprland events! Please file a bug report!"), - }; - - temp_event_holder.push((event_str, event_type, captures)); + let event_iter = event.trim().lines().filter_map(|event_line| { + if event_line.is_empty() { + None + } else { + Some(new_event_parser(event_line)) + } + }); + + let parsed_events = event_iter.map(|event| match event { + Err(x) => Err(x), + Ok(Either::Right((name, args))) => Ok(Event::Unknown(UnknownEventData { name, args })), + Ok(Either::Left((event_type, args))) => match event_type { + ParsedEventType::WorkspaceChangedV2 => { + Ok(Event::WorkspaceChanged(WorkspaceEventData { + id: parse_int!(get![ref args;0], event: "WorkspaceChangedV2"), + name: parse_string_as_work(get![args;1]), + })) } - _ => { - hypr_err!("Event matched more than one regex (not an unknown event issue!)"); + ParsedEventType::WorkspaceDeletedV2 => { + Ok(Event::WorkspaceDeleted(WorkspaceEventData { + id: parse_int!(get![ref args;0], event: "WorkspaceDeletedV2"), + name: parse_string_as_work(get![args;1]), + })) } - } - } - - let parsed_events = temp_event_holder - .into_iter() - .map(|(event_str, event_type, captures)| match event_type { - ParsedEventType::WorkspaceChanged => { - let captured = &captures["workspace"]; - let workspace = if !captured.is_empty() { - parse_string_as_work(captured.to_string()) - } else { - WorkspaceType::Regular("1".to_string()) - }; - Ok(Event::WorkspaceChanged(workspace)) - } - ParsedEventType::WorkspaceDeletedV2 => Ok(Event::WorkspaceDeleted(WorkspaceDestroyedEventData { workspace_id: captures["id"].parse::<WorkspaceId>().map_err(|e| HyprError::Internal(format!("Workspace delete v2: invalid integer error: {e}")))?, workspace_name: captures["name"].to_string() })), - ParsedEventType::WorkspaceAdded => Ok(Event::WorkspaceAdded(parse_string_as_work( - captures["workspace"].to_string(), - ))), - ParsedEventType::WorkspaceMoved => Ok(Event::WorkspaceMoved(MonitorEventData { - monitor_name: captures["monitor"].to_string(), - workspace: parse_string_as_work(captures["workspace"].to_string()), + ParsedEventType::WorkspaceAddedV2 => Ok(Event::WorkspaceAdded(WorkspaceEventData { + id: parse_int!(get![ref args;0], event: "WorkspaceAddedV2"), + name: parse_string_as_work(get![args;1]), })), + ParsedEventType::WorkspaceMovedV2 => { + Ok(Event::WorkspaceMoved(WorkspaceMovedEventData { + id: parse_int!(get![ref args;0], event: "WorkspaceMovedV2"), + name: parse_string_as_work(get![args;1]), + monitor: get![args;2], + })) + } ParsedEventType::WorkspaceRename => { - Ok(Event::WorkspaceRename(WorkspaceRenameEventData { - workspace_id: captures["id"] - .parse::<WorkspaceId>() - .map_err(|e| HyprError::Internal(format!("Workspace rename: invalid integer error: {e}")))?, - workspace_name: captures["name"].to_string(), + Ok(Event::WorkspaceRenamed(NonSpecialWorkspaceEventData { + id: parse_int!(get![args;0], event: "WorkspaceRenamed"), + name: get![args;1], })) } ParsedEventType::ActiveMonitorChanged => { Ok(Event::ActiveMonitorChanged(MonitorEventData { - monitor_name: captures["monitor"].to_string(), - workspace: WorkspaceType::Regular(captures["workspace"].to_string()), + monitor_name: get![args;0], + workspace_name: if get![args;1] == "?" { + None + } else { + Some(parse_string_as_work(get![args;1])) + }, })) } ParsedEventType::ActiveWindowChangedV1 => { - let class = &captures["class"]; - let title = &captures["title"]; + let class = get![args;0]; + let title = get![args;1]; let event = if !class.is_empty() && !title.is_empty() { - Event::ActiveWindowChangedV1(Some((class.to_string(), title.to_string()))) + Event::ActiveWindowChangedV1(Some((class, title))) } else { Event::ActiveWindowChangedV1(None) }; @@ -793,119 +806,111 @@ pub(crate) fn event_parser(event: String) -> crate::Result<Vec<Event>> { Ok(event) } ParsedEventType::ActiveWindowChangedV2 => { - let addr = &captures["address"]; + let addr = get![ref args;0]; let event = if addr != "," { - Event::ActiveWindowChangedV2(Some(Address::fmt_new(addr))) + Event::ActiveWindowChangedV2(Some(Address::new(addr))) } else { Event::ActiveWindowChangedV2(None) }; Ok(event) } ParsedEventType::FullscreenStateChanged => { - let state = &captures["state"] != "0"; - Ok(Event::FullscreenStateChanged(state)) - } - ParsedEventType::MonitorRemoved => { - Ok(Event::MonitorRemoved(captures["monitor"].to_string())) - } - ParsedEventType::MonitorAdded => { - Ok(Event::MonitorAdded(captures["monitor"].to_string())) + Ok(Event::FullscreenStateChanged(get![ref args;0] != "0")) } + ParsedEventType::MonitorRemoved => Ok(Event::MonitorRemoved(get![args;0])), + ParsedEventType::MonitorAddedV2 => Ok(Event::MonitorAdded(MonitorAddedEventData { + id: parse_int!(get![ref args;0], event: "MonitorAddedV2" => u8), + name: get![args;1], + description: get![args;2], + })), ParsedEventType::WindowOpened => Ok(Event::WindowOpened(WindowOpenEvent { - window_address: Address::fmt_new(&captures["address"]), - workspace_name: captures["workspace"].to_string(), - window_class: captures["class"].to_string(), - window_title: captures["title"].to_string(), + window_address: Address::new(get![ref args;0]), + workspace_name: get![args;1], + window_class: get![args;2], + window_title: get![args;3], })), - ParsedEventType::WindowClosed => Ok(Event::WindowClosed(Address::fmt_new(&captures["address"]))), - ParsedEventType::WindowMoved => Ok(Event::WindowMoved(WindowMoveEvent { - window_address: Address::fmt_new(&captures["address"]), - workspace_name: captures["workspace"].to_string(), + ParsedEventType::WindowClosed => Ok(Event::WindowClosed(Address::new(get![args;0]))), + ParsedEventType::WindowMovedV2 => Ok(Event::WindowMoved(WindowMoveEvent { + window_address: Address::fmt_new(get![ref args;0]), + workspace_id: parse_int!(get![ref args;1], event: "WindowMoved"), + workspace_name: parse_string_as_work(get![args;2]), })), ParsedEventType::ActiveSpecial => { - let work = &captures["workspace"]; - if work.is_empty() { - Ok(Event::SpecialRemoved(work.to_string())) + let workspace_name = get![args;0]; + let monitor_name = get![args;1]; + if workspace_name.is_empty() { + Ok(Event::SpecialRemoved(monitor_name)) } else { - let workspace = parse_string_as_work(work.to_string()); - let monitor = &captures["monitor"]; - Ok(Event::ChangedSpecial(MonitorEventData { - monitor_name: monitor.to_string(), - workspace, + Ok(Event::ChangedSpecial(ChangedSpecialEventData { + monitor_name, + workspace_name, })) } } ParsedEventType::LayoutChanged => Ok(Event::LayoutChanged(LayoutEvent { - keyboard_name: captures["keyboard"].to_string(), - layout_name: captures["layout"].to_string(), + keyboard_name: get![args;0], + layout_name: get![args;1], })), - ParsedEventType::SubMapChanged => { - Ok(Event::SubMapChanged(captures["submap"].to_string())) - } - ParsedEventType::LayerOpened => { - Ok(Event::LayerOpened(captures["namespace"].to_string())) - } - ParsedEventType::LayerClosed => { - Ok(Event::LayerClosed(captures["namespace"].to_string())) - } + ParsedEventType::SubMapChanged => Ok(Event::SubMapChanged(get![args;0])), + ParsedEventType::LayerOpened => Ok(Event::LayerOpened(get![args;0])), + ParsedEventType::LayerClosed => Ok(Event::LayerClosed(get![args;0])), ParsedEventType::FloatStateChanged => { - let state = &captures["floatstate"] == "0"; // FIXME: does 0 mean it's floating? + let state = get![ref args;1] == "0"; // FIXME: does 0 mean it's floating? Ok(Event::FloatStateChanged(WindowFloatEventData { - window_address: Address::fmt_new(&captures["address"]), - is_floating: state, + address: Address::new(get![ref args;0]), + floating: state, })) } ParsedEventType::Minimize => { - let state = &captures["state"] == "1"; + let state = get![ref args;1] == "1"; Ok(Event::Minimize(MinimizeEventData { - window_address: Address::fmt_new(&captures["address"]), - is_minimized: state, + window_address: Address::new(get![ref args;0]), + minimized: state, })) } ParsedEventType::Screencast => { - let state = &captures["state"] == "1"; - let owner = &captures["owner"] == "1"; + let state = get![ref args;0] == "1"; + let owner = get![ref args;1] == "1"; Ok(Event::Screencast(ScreencastEventData { - is_turning_on: state, - is_monitor: owner, + turning_on: state, + monitor: owner, + })) + } + ParsedEventType::UrgentStateChanged => { + Ok(Event::UrgentStateChanged(Address::new(get![ref args;0]))) + } + ParsedEventType::WindowTitleChangedV2 => { + Ok(Event::WindowTitleChanged(WindowTitleEventData { + address: Address::new(get![ref args;0]), + title: get![args;1], })) } - ParsedEventType::UrgentStateChanged => Ok(Event::UrgentStateChanged(Address::fmt_new(&captures["address"]))), - ParsedEventType::WindowTitleChanged => Ok(Event::WindowTitleChanged(Address::fmt_new(&captures["address"]))), ParsedEventType::ConfigReloaded => Ok(Event::ConfigReloaded), - ParsedEventType::IgnoreGroupLock => Ok(Event::IgnoreGroupLockStateChanged(&captures["state"] == "1")), - ParsedEventType::LockGroups => Ok(Event::LockGroupsStateChanged(&captures["state"] == "1")), - ParsedEventType::Pin => Ok(Event::WindowPinned(WindowPinEventData { - window_address: Address::fmt_new(&captures["address"]), - is_pinned: &captures["state"] == "1" + ParsedEventType::IgnoreGroupLock => { + Ok(Event::IgnoreGroupLockStateChanged(get![ref args;0] == "1")) + } + ParsedEventType::LockGroups => { + Ok(Event::LockGroupsStateChanged(get![ref args;0] == "1")) + } + ParsedEventType::Pin => Ok(Event::WindowPinned(WindowPinEventData { + address: Address::new(get![ref args;0]), + pinned: get![ref args;1] == "1", })), - ParsedEventType::Unknown => { - #[cfg(not(feature = "silent"))] - { - let table = CHECK_TABLE.lock(); - // The std mutex returns a Result, the parking_lot mutex does not. This is a hack that allows us to - // keep the table code how it is, without duplicating or `return`ing. - #[cfg(feature = "parking_lot")] - let table = Ok::<_, std::convert::Infallible>(table); - - if let Ok(mut tbl) = table { - let (event_string, print_str) = - match captures.name("event").map(|s| s.as_str()) { - Some(s) => (s.to_string(), s), - None => ("Unknown".to_owned(), event_str), - }; - - let should_run = tbl.insert(event_string); - if should_run { - eprintln!( - "An unknown event was passed into Hyprland-rs\nPLEASE MAKE AN ISSUE!!\nThe event was: {print_str}" - ); - } - } - } - hypr_err!("Unknown event: {event_str}"); + ParsedEventType::ToggleGroup => Ok(Event::GroupToggled(GroupToggledEventData { + toggled: get![ref args;0] == "1", + window_addresses: get![ref args;1] + .split(",") + .map(|x| Address::new(x)) + .collect(), + })), + ParsedEventType::MoveIntoGroup => { + Ok(Event::WindowMovedIntoGroup(Address::new(get![ref args;0]))) } - }); + ParsedEventType::MoveOutOfGroup => { + Ok(Event::WindowMovedOutOfGroup(Address::new(get![ref args;0]))) + } + }, + }); let mut events: Vec<Event> = Vec::new(); @@ -913,9 +918,5 @@ pub(crate) fn event_parser(event: String) -> crate::Result<Vec<Event>> { events.push(event?); } - // if events.is_empty() { - // hypr_err!("No events!"); - // } - Ok(events) } diff --git a/src/event_listener/stream.rs b/src/event_listener/stream.rs new file mode 100644 index 0000000..7baa192 --- /dev/null +++ b/src/event_listener/stream.rs @@ -0,0 +1,73 @@ +use super::*; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use async_stream::try_stream; +use futures_lite::{Stream, StreamExt}; + +/// Event listener, but [Stream] +/// This is the new prefered way of listening for events +/// as its more idiomatic, and allows for more efficient memory management +/// +/// # Examples +/// ```rust +/// use hyprland::prelude::*; +/// use hyprland::event_listener::EventStream; +/// use hyprland::Result as HResult; +/// use futures_lite::StreamExt; +/// +/// #[tokio::main] +/// async fn main() -> HResult<()> { +/// let mut stream = EventStream::new(); +/// while let Some(Ok(event)) = stream.next().await { +/// println!("{event:?}"); +/// } +/// } +/// ``` +#[must_use = "streams nothing unless polled"] +pub struct EventStream { + stream: Pin<Box<dyn Stream<Item = crate::Result<Event>>>>, +} +impl EventStream { + /// Creates a new [EventListener] + pub fn new() -> Self { + use crate::unix_async::*; + let stream = try_stream! { + + let socket_path = get_socket_path(SocketType::Listener)?; + let mut stream = UnixStream::connect(socket_path).await?; + + let mut active_windows = vec![]; + loop { + let mut buf = [0; 4096]; + + let num_read = stream.read(&mut buf).await?; + if num_read == 0 { + break; + } + let buf = &buf[..num_read]; + let string = String::from_utf8(buf.to_vec())?; + let parsed: Vec<Event> = event_parser(string)?; + + for event in parsed { + for primed_event in event_primer_noexec(event, &mut active_windows)? { + yield primed_event; + } + } + } + }; + Self { + stream: Box::pin(stream), + } + } +} + +impl Stream for EventStream { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + self.as_mut().stream.poll_next(cx) + } + + type Item = crate::Result<Event>; +} diff --git a/src/lib.rs b/src/lib.rs index 6e10dda..0f13a67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,10 @@ #[macro_use] extern crate paste; -pub use hyprland_macros::*; +#[macro_use] +extern crate hyprland_macros; + +pub use hyprland_macros::async_closure; /// This module provides several impls that are unsafe, for FFI purposes. Only use if you know what you are doing. #[cfg(feature = "unsafe-impl")] diff --git a/src/shared.rs b/src/shared.rs index f9ec13e..7242d7e 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -91,10 +91,6 @@ macro_rules! hypr_err { pub(crate) use hypr_err; -/// This type provides the result type used everywhere in Hyprland-rs -#[deprecated(since = "0.3.1", note = "New location: hyprland::Result")] -pub type HResult<T> = Result<T, HyprError>; - /// The address struct holds a address as a tuple with a single value /// and has methods to reveal the address in different data formats #[derive( @@ -109,7 +105,12 @@ impl Address { } /// This creates a new address from a value that implements [std::string::ToString] pub fn new<T: ToString>(string: T) -> Self { - Self(string.to_string()) + let str = string.to_string(); + if str.starts_with("0x") { + Self(str) + } else { + Self("0x".to_owned() + str.as_str()) + } } }