diff --git a/Cargo.toml b/Cargo.toml index c59fb03..3da9bbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,11 +30,11 @@ default = [ "tray", "upower", "volume", - "workspaces+all" + "workspaces+all", ] -cli = ["dep:clap", "ipc"] -ipc = ["dep:serde_json"] +cli = ["ipc"] +ipc = ["dep:serde_json", "dep:clap"] http = ["dep:reqwest"] @@ -61,7 +61,7 @@ custom = [] focused = [] -keyboard = ["dep:colpetto", "dep:evdev-rs", "dep:rustix"] +keyboard = ["dep:colpetto", "dep:evdev-rs", "dep:rustix", "futures-lite"] "keyboard+all" = ["keyboard", "keyboard+sway", "keyboard+hyprland"] "keyboard+sway" = ["keyboard", "sway"] "keyboard+hyprland" = ["keyboard", "hyprland"] @@ -93,10 +93,12 @@ workspaces = ["futures-lite"] "workspaces+all" = ["workspaces", "workspaces+sway", "workspaces+hyprland", "workspaces+niri"] "workspaces+sway" = ["workspaces", "sway"] "workspaces+hyprland" = ["workspaces", "hyprland"] -"workspaces+niri" = ["workspaces"] +"workspaces+niri" = ["workspaces", "niri"] sway = ["swayipc-async", "futures-lite"] +niri = ["dep:serde_json"] + schema = ["dep:schemars"] [dependencies] @@ -136,9 +138,6 @@ cfg-if = "1.0.0" # cli clap = { version = "4.5.36", optional = true, features = ["derive"] } -# ipc -serde_json = { version = "1.0.140", optional = true } - # http reqwest = { version = "0.12.15", default-features = false, features = ["default-tls", "http2"], optional = true } @@ -174,11 +173,12 @@ system-tray = { version = "0.7.0", features = ["dbusmenu-gtk3"], optional = true libpulse-binding = { version = "2.29.0", optional = true } # shared -futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces +futures-lite = { version = "2.6.0", optional = true } # network_manager, upower, workspaces, keyboard zbus = { version = "5.5.0", default-features = false, features = ["tokio"], optional = true } # network_manager, notifications, upower swayipc-async = { version = "2.0.4", optional = true } # workspaces, keyboard hyprland = { version = "0.4.0-alpha.3", features = ["silent"], optional = true } # workspaces, keyboard rustix = { version = "1.0.5", default-features = false, features = ["std", "fs", "pipe", "event"], optional = true } # clipboard, input +serde_json = { version = "1.0.140", optional = true } # ipc, niri # schema schemars = { version = "0.8.22", optional = true } diff --git a/src/clients/compositor/hyprland.rs b/src/clients/compositor/hyprland.rs index d7bb91c..f205d6d 100644 --- a/src/clients/compositor/hyprland.rs +++ b/src/clients/compositor/hyprland.rs @@ -1,4 +1,4 @@ -use super::{KeyboardLayoutClient, KeyboardLayoutUpdate, Visibility, Workspace, WorkspaceUpdate}; +use super::{Visibility, Workspace, WorkspaceUpdate}; use crate::{arc_mut, lock, send, spawn_blocking}; use color_eyre::Result; use hyprland::ctl::switch_xkb_layout; @@ -11,35 +11,53 @@ use tokio::sync::broadcast::{Receiver, Sender, channel}; use tracing::{debug, error, info}; #[derive(Debug)] -pub struct Client { - workspace_tx: Sender, - _workspace_rx: Receiver, +struct TxRx { + tx: Sender, + _rx: Receiver, +} - keyboard_layout_tx: Sender, - _keyboard_layout_rx: Receiver, +#[derive(Debug)] +pub struct Client { + #[cfg(feature = "workspaces+hyprland")] + workspace: TxRx, + + #[cfg(feature = "keyboard+hyprland")] + keyboard_layout: TxRx, } impl Client { pub(crate) fn new() -> Self { + #[cfg(feature = "workspaces+hyprland")] let (workspace_tx, workspace_rx) = channel(16); + + #[cfg(feature = "keyboard+hyprland")] let (keyboard_layout_tx, keyboard_layout_rx) = channel(16); let instance = Self { - workspace_tx, - _workspace_rx: workspace_rx, - keyboard_layout_tx, - _keyboard_layout_rx: keyboard_layout_rx, + #[cfg(feature = "workspaces+hyprland")] + workspace: TxRx { + tx: workspace_tx, + _rx: workspace_rx, + }, + #[cfg(feature = "keyboard+hyprland")] + keyboard_layout: TxRx { + tx: keyboard_layout_tx, + _rx: keyboard_layout_rx, + }, }; - instance.listen_workspace_events(); + instance.listen_events(); instance } - fn listen_workspace_events(&self) { + fn listen_events(&self) { info!("Starting Hyprland event listener"); - let tx = self.workspace_tx.clone(); - let keyboard_layout_tx = self.keyboard_layout_tx.clone(); + #[cfg(feature = "workspaces+hyprland")] + let tx = self.workspace.tx.clone(); + + #[cfg(feature = "keyboard+hyprland")] + let keyboard_layout_tx = self.keyboard_layout.tx.clone(); spawn_blocking(move || { let mut event_listener = EventListener::new(); @@ -48,223 +66,11 @@ impl Client { let lock = arc_mut!(()); // cache the active workspace since Hyprland doesn't give us the prev active - let active = Self::get_active_workspace().expect("Failed to get active workspace"); - let active = arc_mut!(Some(active)); + #[cfg(feature = "workspaces+hyprland")] + Self::listen_workspace_events(tx, &mut event_listener, &lock); - { - let tx = tx.clone(); - let lock = lock.clone(); - let active = active.clone(); - - event_listener.add_workspace_added_handler(move |workspace_type| { - let _lock = lock!(lock); - debug!("Added workspace: {workspace_type:?}"); - - let workspace_name = get_workspace_name(workspace_type); - let prev_workspace = lock!(active); - - let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); - - if let Some(workspace) = workspace { - send!(tx, WorkspaceUpdate::Add(workspace)); - } - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - let active = active.clone(); - - event_listener.add_workspace_change_handler(move |workspace_type| { - let _lock = lock!(lock); - - let mut prev_workspace = lock!(active); - - debug!( - "Received workspace change: {:?} -> {workspace_type:?}", - prev_workspace.as_ref().map(|w| &w.id) - ); - - let workspace_name = get_workspace_name(workspace_type); - let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); - - workspace.map_or_else( - || { - error!("Unable to locate workspace"); - }, - |workspace| { - // there may be another type of update so dispatch that regardless of focus change - if !workspace.visibility.is_focused() { - Self::send_focus_change(&mut prev_workspace, workspace, &tx); - } - }, - ); - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - let active = active.clone(); - - event_listener.add_active_monitor_change_handler(move |event_data| { - let _lock = lock!(lock); - let workspace_type = event_data.workspace; - - let mut prev_workspace = lock!(active); - - debug!( - "Received active monitor change: {:?} -> {workspace_type:?}", - prev_workspace.as_ref().map(|w| &w.name) - ); - - let workspace_name = get_workspace_name(workspace_type); - let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); - - if let Some((false, workspace)) = - workspace.map(|w| (w.visibility.is_focused(), w)) - { - Self::send_focus_change(&mut prev_workspace, workspace, &tx); - } else { - error!("unable to locate workspace: {workspace_name}"); - } - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - - event_listener.add_workspace_moved_handler(move |event_data| { - let _lock = lock!(lock); - let workspace_type = event_data.workspace; - debug!("Received workspace move: {workspace_type:?}"); - - let mut prev_workspace = lock!(active); - - let workspace_name = get_workspace_name(workspace_type); - let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); - - if let Some(workspace) = workspace { - send!(tx, WorkspaceUpdate::Move(workspace.clone())); - - if !workspace.visibility.is_focused() { - Self::send_focus_change(&mut prev_workspace, workspace, &tx); - } - } - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - - event_listener.add_workspace_rename_handler(move |data| { - let _lock = lock!(lock); - debug!("Received workspace rename: {data:?}"); - - send!( - tx, - WorkspaceUpdate::Rename { - id: data.workspace_id as i64, - name: data.workspace_name - } - ); - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - - event_listener.add_workspace_destroy_handler(move |data| { - let _lock = lock!(lock); - debug!("Received workspace destroy: {data:?}"); - send!(tx, WorkspaceUpdate::Remove(data.workspace_id as i64)); - }); - } - - { - let tx = tx.clone(); - let lock = lock.clone(); - - event_listener.add_urgent_state_handler(move |address| { - let _lock = lock!(lock); - debug!("Received urgent state: {address:?}"); - - let clients = match hyprland::data::Clients::get() { - Ok(clients) => clients, - Err(err) => { - error!("Failed to get clients: {err}"); - return; - } - }; - clients.iter().find(|c| c.address == address).map_or_else( - || { - error!("Unable to locate client"); - }, - |c| { - send!( - tx, - WorkspaceUpdate::Urgent { - id: c.workspace.id as i64, - urgent: true, - } - ); - }, - ); - }); - } - - { - let tx = keyboard_layout_tx.clone(); - let lock = lock.clone(); - - event_listener.add_keyboard_layout_change_handler(move |layout_event| { - let _lock = lock!(lock); - - let layout = if layout_event.layout_name.is_empty() { - // FIXME: This field is empty due to bug in `hyprland-rs_0.4.0-alpha.3`. Which is already fixed in last betas - - // The layout may be empty due to a bug in `hyprland-rs`, because of which the `layout_event` is incorrect. - // - // Instead of: - // ``` - // LayoutEvent { - // keyboard_name: "keychron-keychron-c2", - // layout_name: "English (US)", - // } - // ``` - // - // We get: - // ``` - // LayoutEvent { - // keyboard_name: "keychron-keychron-c2,English (US)", - // layout_name: "", - // } - // ``` - // - // Here we are trying to recover `layout_name` from `keyboard_name` - - let layout = layout_event.keyboard_name.as_str().split(',').nth(1); - let Some(layout) = layout else { - error!( - "Failed to get layout from string: {}. The failed logic is a workaround for a bug in `hyprland 0.4.0-alpha.3`", layout_event.keyboard_name); - return; - }; - - layout.into() - } - else { - layout_event.layout_name - }; - - debug!("Received layout: {layout:?}"); - - send!(tx, KeyboardLayoutUpdate(layout)); - }); - } + #[cfg(feature = "keyboard+hyprland")] + Self::listen_keyboard_events(keyboard_layout_tx, &mut event_listener, lock); event_listener .start_listener() @@ -272,8 +78,238 @@ impl Client { }); } + #[cfg(feature = "workspaces+hyprland")] + fn listen_workspace_events( + tx: Sender, + event_listener: &mut EventListener, + lock: &std::sync::Arc>, + ) { + let active = Self::get_active_workspace().expect("Failed to get active workspace"); + let active = arc_mut!(Some(active)); + + { + let tx = tx.clone(); + let lock = lock.clone(); + let active = active.clone(); + + event_listener.add_workspace_added_handler(move |workspace_type| { + let _lock = lock!(lock); + debug!("Added workspace: {workspace_type:?}"); + + let workspace_name = get_workspace_name(workspace_type); + let prev_workspace = lock!(active); + + let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); + + if let Some(workspace) = workspace { + send!(tx, WorkspaceUpdate::Add(workspace)); + } + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + let active = active.clone(); + + event_listener.add_workspace_change_handler(move |workspace_type| { + let _lock = lock!(lock); + + let mut prev_workspace = lock!(active); + + debug!( + "Received workspace change: {:?} -> {workspace_type:?}", + prev_workspace.as_ref().map(|w| &w.id) + ); + + let workspace_name = get_workspace_name(workspace_type); + let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); + + workspace.map_or_else( + || { + error!("Unable to locate workspace"); + }, + |workspace| { + // there may be another type of update so dispatch that regardless of focus change + if !workspace.visibility.is_focused() { + Self::send_focus_change(&mut prev_workspace, workspace, &tx); + } + }, + ); + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + let active = active.clone(); + + event_listener.add_active_monitor_change_handler(move |event_data| { + let _lock = lock!(lock); + let workspace_type = event_data.workspace; + + let mut prev_workspace = lock!(active); + + debug!( + "Received active monitor change: {:?} -> {workspace_type:?}", + prev_workspace.as_ref().map(|w| &w.name) + ); + + let workspace_name = get_workspace_name(workspace_type); + let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); + + if let Some((false, workspace)) = workspace.map(|w| (w.visibility.is_focused(), w)) + { + Self::send_focus_change(&mut prev_workspace, workspace, &tx); + } else { + error!("unable to locate workspace: {workspace_name}"); + } + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + + event_listener.add_workspace_moved_handler(move |event_data| { + let _lock = lock!(lock); + let workspace_type = event_data.workspace; + debug!("Received workspace move: {workspace_type:?}"); + + let mut prev_workspace = lock!(active); + + let workspace_name = get_workspace_name(workspace_type); + let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref()); + + if let Some(workspace) = workspace { + send!(tx, WorkspaceUpdate::Move(workspace.clone())); + + if !workspace.visibility.is_focused() { + Self::send_focus_change(&mut prev_workspace, workspace, &tx); + } + } + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + + event_listener.add_workspace_rename_handler(move |data| { + let _lock = lock!(lock); + debug!("Received workspace rename: {data:?}"); + + send!( + tx, + WorkspaceUpdate::Rename { + id: data.workspace_id as i64, + name: data.workspace_name + } + ); + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + + event_listener.add_workspace_destroy_handler(move |data| { + let _lock = lock!(lock); + debug!("Received workspace destroy: {data:?}"); + send!(tx, WorkspaceUpdate::Remove(data.workspace_id as i64)); + }); + } + + { + let tx = tx.clone(); + let lock = lock.clone(); + + event_listener.add_urgent_state_handler(move |address| { + let _lock = lock!(lock); + debug!("Received urgent state: {address:?}"); + + let clients = match hyprland::data::Clients::get() { + Ok(clients) => clients, + Err(err) => { + error!("Failed to get clients: {err}"); + return; + } + }; + clients.iter().find(|c| c.address == address).map_or_else( + || { + error!("Unable to locate client"); + }, + |c| { + send!( + tx, + WorkspaceUpdate::Urgent { + id: c.workspace.id as i64, + urgent: true, + } + ); + }, + ); + }); + } + } + + #[cfg(feature = "keyboard+hyprland")] + fn listen_keyboard_events( + keyboard_layout_tx: Sender, + event_listener: &mut EventListener, + lock: std::sync::Arc>, + ) { + let tx = keyboard_layout_tx.clone(); + let lock = lock.clone(); + + event_listener.add_keyboard_layout_change_handler(move |layout_event| { + let _lock = lock!(lock); + + let layout = if layout_event.layout_name.is_empty() { + // FIXME: This field is empty due to bug in `hyprland-rs_0.4.0-alpha.3`. Which is already fixed in last betas + + // The layout may be empty due to a bug in `hyprland-rs`, because of which the `layout_event` is incorrect. + // + // Instead of: + // ``` + // LayoutEvent { + // keyboard_name: "keychron-keychron-c2", + // layout_name: "English (US)", + // } + // ``` + // + // We get: + // ``` + // LayoutEvent { + // keyboard_name: "keychron-keychron-c2,English (US)", + // layout_name: "", + // } + // ``` + // + // Here we are trying to recover `layout_name` from `keyboard_name` + + let layout = layout_event.keyboard_name.as_str().split(',').nth(1); + let Some(layout) = layout else { + error!( + "Failed to get layout from string: {}. The failed logic is a workaround for a bug in `hyprland 0.4.0-alpha.3`", layout_event.keyboard_name); + return; + }; + + layout.into() + } + else { + layout_event.layout_name + }; + + debug!("Received layout: {layout:?}"); + + send!(tx, KeyboardLayoutUpdate(layout)); + }); + } + /// Sends a `WorkspaceUpdate::Focus` event /// and updates the active workspace cache. + #[cfg(feature = "workspaces+hyprland")] fn send_focus_change( prev_workspace: &mut Option, workspace: Workspace, @@ -299,6 +335,7 @@ impl Client { } /// Gets a workspace by name from the server, given the active workspace if known. + #[cfg(feature = "workspaces+hyprland")] fn get_workspace(name: &str, active: Option<&Workspace>) -> Option { Workspaces::get() .expect("Failed to get workspaces") @@ -323,7 +360,7 @@ impl Client { } } -#[cfg(feature = "workspaces")] +#[cfg(feature = "workspaces+hyprland")] impl super::WorkspaceClient for Client { fn focus(&self, id: i64) { let identifier = WorkspaceIdentifierWithSpecial::Id(id as i32); @@ -334,7 +371,7 @@ impl super::WorkspaceClient for Client { } fn subscribe(&self) -> Receiver { - let rx = self.workspace_tx.subscribe(); + let rx = self.workspace.tx.subscribe(); let active_id = HWorkspace::get_active().ok().map(|active| active.name); let is_visible = create_is_visible(); @@ -349,12 +386,16 @@ impl super::WorkspaceClient for Client { }) .collect(); - send!(self.workspace_tx, WorkspaceUpdate::Init(workspaces)); + send!(self.workspace.tx, WorkspaceUpdate::Init(workspaces)); rx } } +#[cfg(feature = "keyboard+hyprland")] +use super::{KeyboardLayoutClient, KeyboardLayoutUpdate}; + +#[cfg(feature = "keyboard+hyprland")] impl KeyboardLayoutClient for Client { fn set_next_active(&self) { let device = Devices::get() @@ -376,7 +417,7 @@ impl KeyboardLayoutClient for Client { } fn subscribe(&self) -> Receiver { - let rx = self.keyboard_layout_tx.subscribe(); + let rx = self.keyboard_layout.tx.subscribe(); let layout = Devices::get() .expect("Failed to get devices") @@ -386,7 +427,7 @@ impl KeyboardLayoutClient for Client { .map(|k| k.active_keymap.clone()); if let Some(layout) = layout { - send!(self.keyboard_layout_tx, KeyboardLayoutUpdate(layout)); + send!(self.keyboard_layout.tx, KeyboardLayoutUpdate(layout)); } else { error!("Failed to get current keyboard layout hyprland"); } diff --git a/src/clients/compositor/mod.rs b/src/clients/compositor/mod.rs index cb21d53..f413b99 100644 --- a/src/clients/compositor/mod.rs +++ b/src/clients/compositor/mod.rs @@ -7,19 +7,19 @@ use std::sync::Arc; use tokio::sync::broadcast; use tracing::debug; -#[cfg(any(feature = "keyboard+hyprland", feature = "workspaces+hyprland"))] +#[cfg(feature = "hyprland")] pub mod hyprland; -#[cfg(feature = "workspaces+niri")] +#[cfg(feature = "niri")] pub mod niri; -#[cfg(any(feature = "keyboard+sway", feature = "workspaces+sway"))] +#[cfg(feature = "sway")] pub mod sway; pub enum Compositor { - #[cfg(any(feature = "keyboard+sway", feature = "workspaces+sway"))] + #[cfg(feature = "sway")] Sway, - #[cfg(any(feature = "keyboard+hyprland", feature = "workspaces+hyprland"))] + #[cfg(feature = "hyprland")] Hyprland, - #[cfg(feature = "workspaces+niri")] + #[cfg(feature = "niri")] Niri, Unsupported, } @@ -30,9 +30,9 @@ impl Display for Compositor { f, "{}", match self { - #[cfg(any(feature = "keyboard+sway", feature = "workspaces+sway"))] + #[cfg(any(feature = "sway"))] Self::Sway => "Sway", - #[cfg(any(feature = "keyboard+hyprland", feature = "workspaces+hyprland"))] + #[cfg(any(feature = "hyprland"))] Self::Hyprland => "Hyprland", #[cfg(feature = "workspaces+niri")] Self::Niri => "Niri", @@ -48,17 +48,17 @@ impl Compositor { fn get_current() -> Self { if std::env::var("SWAYSOCK").is_ok() { cfg_if! { - if #[cfg(any(feature = "keyboard+sway", feature = "workspaces+sway"))] { Self::Sway } + if #[cfg(feature = "sway")] { Self::Sway } else { tracing::error!("Not compiled with Sway support"); Self::Unsupported } } } else if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() { cfg_if! { - if #[cfg(any(feature = "keyboard+hyprland", feature = "workspaces+hyprland"))] { Self::Hyprland } + if #[cfg(feature = "hyprland")] { Self::Hyprland } else { tracing::error!("Not compiled with Hyprland support"); Self::Unsupported } } } else if std::env::var("NIRI_SOCKET").is_ok() { cfg_if! { - if #[cfg(feature = "workspaces+niri")] { Self::Niri } + if #[cfg(feature = "niri")] { Self::Niri } else {tracing::error!("Not compiled with Niri support"); Self::Unsupported } } } else { @@ -79,13 +79,16 @@ impl Compositor { .map(|client| client as Arc), #[cfg(feature = "keyboard+hyprland")] Self::Hyprland => Ok(clients.hyprland()), - #[cfg(feature = "workspaces")] + #[cfg(feature = "niri")] Self::Niri => Err(Report::msg("Unsupported compositor").note( "Currently keyboard layout functionality are only supported by Sway and Hyprland", )), Self::Unsupported => Err(Report::msg("Unsupported compositor").note( "Currently keyboard layout functionality are only supported by Sway and Hyprland", )), + #[allow(unreachable_patterns)] + _ => Err(Report::msg("Unsupported compositor") + .note("Keyboard layout feature is disabled for this compositor")), } } @@ -108,6 +111,9 @@ impl Compositor { Self::Niri => Ok(Arc::new(niri::Client::new())), Self::Unsupported => Err(Report::msg("Unsupported compositor") .note("Currently workspaces are only supported by Sway, Niri and Hyprland")), + #[allow(unreachable_patterns)] + _ => Err(Report::msg("Unsupported compositor") + .note("Workspaces feature is disabled for this compositor")), } } } diff --git a/src/clients/compositor/sway.rs b/src/clients/compositor/sway.rs index 42d7176..5e01575 100644 --- a/src/clients/compositor/sway.rs +++ b/src/clients/compositor/sway.rs @@ -1,11 +1,11 @@ -use super::{KeyboardLayoutClient, KeyboardLayoutUpdate, Visibility, Workspace, WorkspaceUpdate}; +use super::{Visibility, Workspace, WorkspaceUpdate}; use crate::clients::sway::Client; use crate::{await_sync, error, send, spawn}; use color_eyre::Report; use swayipc_async::{InputChange, InputEvent, Node, WorkspaceChange, WorkspaceEvent}; use tokio::sync::broadcast::{Receiver, channel}; -#[cfg(feature = "workspaces")] +#[cfg(feature = "workspaces+sway")] impl super::WorkspaceClient for Client { fn focus(&self, id: i64) { let client = self.connection().clone(); @@ -153,6 +153,10 @@ impl From for WorkspaceUpdate { } } +#[cfg(feature = "keyboard+sway")] +use super::{KeyboardLayoutClient, KeyboardLayoutUpdate}; + +#[cfg(feature = "keyboard+sway")] impl KeyboardLayoutClient for Client { fn set_next_active(&self) { let client = self.connection().clone(); @@ -210,6 +214,7 @@ impl KeyboardLayoutClient for Client { } } +#[cfg(feature = "keyboard+sway")] impl TryFrom for KeyboardLayoutUpdate { type Error = (); diff --git a/src/clients/mod.rs b/src/clients/mod.rs index 25d9979..5ba6392 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -7,7 +7,7 @@ use std::sync::Arc; #[cfg(feature = "clipboard")] pub mod clipboard; -#[cfg(any(feature = "keyboard", feature = "workspaces"))] +#[cfg(any(feature = "keyboard", feature = "workspaces", feature = "hyprland"))] pub mod compositor; #[cfg(feature = "keyboard")] pub mod libinput; @@ -195,7 +195,10 @@ impl Clients { self.sys_info .get_or_insert_with(|| { let client = Arc::new(sysinfo::Client::new()); + + #[cfg(feature = "ipc")] Ironbar::variable_manager().register_namespace("sysinfo", client.clone()); + client }) .clone() diff --git a/src/clients/music/mod.rs b/src/clients/music/mod.rs index e42b60f..52895b7 100644 --- a/src/clients/music/mod.rs +++ b/src/clients/music/mod.rs @@ -70,13 +70,17 @@ pub trait MusicClient: Debug + Send + Sync { #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum ClientType { + #[cfg(feature = "music+mpd")] Mpd { host: String, music_dir: PathBuf }, + #[cfg(feature = "music+mpris")] Mpris, } pub fn create_client(client_type: ClientType) -> Arc { match client_type { + #[cfg(feature = "music+mpd")] ClientType::Mpd { host, music_dir } => Arc::new(mpd::Client::new(host, music_dir)), + #[cfg(feature = "music+mpris")] ClientType::Mpris => Arc::new(mpris::Client::new()), } } diff --git a/src/clients/sysinfo.rs b/src/clients/sysinfo.rs index ac5cce5..a9466c0 100644 --- a/src/clients/sysinfo.rs +++ b/src/clients/sysinfo.rs @@ -1,4 +1,3 @@ -use crate::ironvar::Namespace; use crate::modules::sysinfo::Interval; use crate::{lock, register_client}; use color_eyre::{Report, Result}; @@ -483,6 +482,10 @@ impl FromStr for TokenType { } } +#[cfg(feature = "ipc")] +use crate::ironvar::Namespace; + +#[cfg(feature = "ipc")] impl Namespace for Client { fn get(&self, key: &str) -> Option { let get = |value: Value| Some(value.get(Prefix::None).to_string()); @@ -591,6 +594,7 @@ impl Namespace for Client { } } +#[cfg(feature = "ipc")] impl Namespace for ValueSet { fn get(&self, key: &str) -> Option { let function = Function::from_str(key).ok()?; diff --git a/src/image/gtk.rs b/src/image/gtk.rs index 687428e..04ed103 100644 --- a/src/image/gtk.rs +++ b/src/image/gtk.rs @@ -6,10 +6,13 @@ use std::ops::Deref; #[derive(Debug, Clone)] #[cfg(any( + feature = "cairo", + feature = "clipboard", feature = "clipboard", feature = "keyboard", + feature = "launcher", feature = "music", - feature = "workspaces" + feature = "workspaces", ))] pub struct IconButton { button: Button, @@ -17,10 +20,13 @@ pub struct IconButton { } #[cfg(any( + feature = "cairo", + feature = "clipboard", feature = "clipboard", feature = "keyboard", + feature = "launcher", feature = "music", - feature = "workspaces" + feature = "workspaces", ))] impl IconButton { pub fn new(input: &str, icon_theme: &IconTheme, size: i32) -> Self { @@ -57,6 +63,15 @@ impl IconButton { } } +#[cfg(any( + feature = "clipboard", + feature = "keyboard", + feature = "music", + feature = "workspaces", + feature = "cairo", + feature = "clipboard", + feature = "launcher", +))] impl Deref for IconButton { type Target = Button; @@ -138,6 +153,7 @@ impl IconLabel { } } +#[cfg(any(feature = "keyboard", feature = "music", feature = "workspaces"))] impl Deref for IconLabel { type Target = gtk::Box; diff --git a/src/image/mod.rs b/src/image/mod.rs index c361cf3..0d75b93 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -1,12 +1,19 @@ #[cfg(any( feature = "clipboard", feature = "keyboard", + feature = "launcher", feature = "music", - feature = "workspaces" + feature = "workspaces", ))] mod gtk; mod provider; -#[cfg(any(feature = "keyboard", feature = "music", feature = "workspaces"))] +#[cfg(any( + feature = "clipboard", + feature = "keyboard", + feature = "launcher", + feature = "music", + feature = "workspaces", +))] pub use self::gtk::*; pub use provider::ImageProvider; diff --git a/src/modules/music/config.rs b/src/modules/music/config.rs index 42023ce..d19e0bc 100644 --- a/src/modules/music/config.rs +++ b/src/modules/music/config.rs @@ -74,13 +74,23 @@ impl Default for Icons { #[serde(rename_all = "snake_case")] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum PlayerType { + #[cfg(feature = "music+mpd")] Mpd, + #[cfg(feature = "music+mpris")] Mpris, } impl Default for PlayerType { fn default() -> Self { - Self::Mpris + cfg_if::cfg_if! { + if #[cfg(feature = "music+mpris")] { + Self::Mpris + } else if #[cfg(feature = "music+mpd")] { + Self::Mpd + } else { + compile_error!("No player type feature enabled") + } + } } } diff --git a/src/modules/music/mod.rs b/src/modules/music/mod.rs index bc5b61e..42d018e 100644 --- a/src/modules/music/mod.rs +++ b/src/modules/music/mod.rs @@ -76,7 +76,9 @@ fn get_client( music_dir: PathBuf, ) -> Arc { let client_type = match player_type { + #[cfg(feature = "music+mpd")] PlayerType::Mpd => music::ClientType::Mpd { host, music_dir }, + #[cfg(feature = "music+mpris")] PlayerType::Mpris => music::ClientType::Mpris, };