diff --git a/Cargo.lock b/Cargo.lock index b1e4d5de..863c0505 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3082,6 +3082,7 @@ name = "spotifyd" version = "0.3.0" dependencies = [ "alsa 0.3.0", + "cfg-if 1.0.0", "chrono", "color-eyre", "daemonize", diff --git a/Cargo.toml b/Cargo.toml index 6a5036fd..4c88195f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ xdg = "2.2" librespot = { version = "0.1.1", default-features = false, features = ["with-tremor"] } toml = "0.5.8" color-eyre = "0.5" +cfg-if = "1" [target."cfg(target_os = \"macos\")".dependencies] whoami = "0.9.0" diff --git a/docs/src/config/File.md b/docs/src/config/File.md index 2cc4a487..c45c1e29 100644 --- a/docs/src/config/File.md +++ b/docs/src/config/File.md @@ -42,13 +42,19 @@ backend = "alsa" # The alsa audio device to stream audio to. To get a # list of valid devices, run `aplay -L`, +# +# Comment out or remove if you are not using the `alsa_backend`. device = "alsa_audio_device" # omit for macOS # The alsa control device. By default this is the same # name as the `device` field. +# +# Comment out or remove if you are not using the `alsa_backend`. control = "alsa_audio_device" # omit for macOS # The alsa mixer used by `spotifyd`. +# +# Comment out or remove if you are not using the `alsa_backend`. mixer = "PCM" # The volume controller. Each one behaves different to @@ -114,7 +120,7 @@ device_type = "speaker" - **`use_keyring`** config entry / **`--use-keyring`** CLI flag - This features leverages [Linux's DBus Secret Service API][secret-storage-specification] or native macOS keychain in order to forgo the need to store your password directly in the config file. To use it, complile with the `dbus_keyring` feature and set the `use-keyring` config entry to `true` or pass the `--use-keyring` CLI flag during start to the daemon. Remove the `password` and/or `password_cmd` config entries. + This features leverages [Linux's DBus Secret Service API][secret-storage-specification] or native macOS keychain in order to forgo the need to store your password directly in the config file. To use it, complile with the `dbus_keyring` feature and set the `use-keyring` config entry to `true` or pass the `--use-keyring` CLI flag during start to the daemon. Remove the `password` and/or `password_cmd` config entries. Your keyring entry needs to have the following attributes set: diff --git a/src/config.rs b/src/config.rs index bfa862db..93b40b52 100644 --- a/src/config.rs +++ b/src/config.rs @@ -317,14 +317,17 @@ pub struct SharedConfigValues { volume_controller: Option, /// The audio device + #[cfg(feature = "alsa_backend")] #[structopt(long, value_name = "string")] device: Option, /// The control device + #[cfg(feature = "alsa_backend")] #[structopt(long, value_name = "string")] control: Option, /// The mixer to use + #[cfg(feature = "alsa_backend")] #[structopt(long, value_name = "string")] mixer: Option, @@ -429,7 +432,8 @@ impl fmt::Debug for SharedConfigValues { None }; - f.debug_struct("SharedConfigValues") + let mut deb_struct = f.debug_struct("SharedConfigValues"); + deb_struct .field("username", &username_value) .field("username_cmd", &username_cmd_value) .field("password", &password_value) @@ -441,9 +445,6 @@ impl fmt::Debug for SharedConfigValues { .field("no-audio-cache", &self.no_audio_cache) .field("backend", &self.backend) .field("volume_controller", &self.volume_controller) - .field("device", &self.device) - .field("control", &self.control) - .field("mixer", &self.mixer) .field("device_name", &self.device_name) .field("bitrate", &self.bitrate) .field("initial_volume", &self.initial_volume) @@ -451,8 +452,18 @@ impl fmt::Debug for SharedConfigValues { .field("normalisation_pregain", &self.normalisation_pregain) .field("zeroconf_port", &self.zeroconf_port) .field("proxy", &self.proxy) - .field("device_type", &self.device_type) - .finish() + .field("device_type", &self.device_type); + cfg_if::cfg_if! { + if #[cfg(feature = "alsa_backend")] { + deb_struct + .field("device", &self.device) + .field("control", &self.control) + .field("mixer", &self.mixer) + .finish() + } else { + deb_struct.finish() + } + } } } @@ -494,7 +505,6 @@ impl SharedConfigValues { } } - // Handles Option merging. merge!( backend, username, @@ -505,9 +515,6 @@ impl SharedConfigValues { bitrate, initial_volume, device_name, - mixer, - control, - device, volume_controller, cache_path, on_song_change_hook, @@ -517,6 +524,9 @@ impl SharedConfigValues { use_mpris ); + #[cfg(feature = "alsa_backend")] + merge!(mixer, control, device); + // Handles boolean merging. self.use_keyring |= other.use_keyring; self.volume_normalisation |= other.volume_normalisation; @@ -550,12 +560,13 @@ pub(crate) struct SpotifydConfig { pub(crate) use_mpris: bool, pub(crate) cache: Option, pub(crate) backend: Option, + #[cfg(feature = "alsa_backend")] pub(crate) audio_device: Option, - #[allow(unused)] + #[cfg(feature = "alsa_backend")] pub(crate) control_device: Option, - #[allow(unused)] + #[cfg(feature = "alsa_backend")] pub(crate) mixer: Option, - #[allow(unused)] + #[cfg_attr(not(feature = "alsa_backend"), allow(dead_code))] pub(crate) volume_controller: VolumeController, pub(crate) initial_volume: Option, pub(crate) device_name: String, @@ -683,8 +694,11 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { use_mpris: config.shared_config.use_mpris.unwrap_or(true), cache, backend: Some(backend), + #[cfg(feature = "alsa_backend")] audio_device: config.shared_config.device, + #[cfg(feature = "alsa_backend")] control_device: config.shared_config.control, + #[cfg(feature = "alsa_backend")] mixer: config.shared_config.mixer, volume_controller, initial_volume, @@ -738,4 +752,27 @@ mod tests { spotifyd_section.username = Some("testUserName".to_string()); assert_eq!(merged_config, spotifyd_section); } + + #[cfg(features = "alsa_backend")] + #[test] + fn test_section_merging() { + let mut spotifyd_section = SharedConfigValues::default(); + global_section.mixer = "PCM".to_string(); + + let global_section = SharedConfigValues::default(); + global_section.mixer = "PCM1".to_string(); + + // The test only makes sense if both sections differ. + assert!(spotifyd_section != global_section, true); + + let file_config = FileConfig { + global: Some(global_section), + spotifyd: Some(spotifyd_section.clone()), + }; + let merged_config = file_config.get_merged_sections().unwrap(); + + // Add the new field to spotifyd section. + spotifyd_section.mixer = "PMC".to_string(); + assert_eq!(merged_config, spotifyd_section); + } } diff --git a/src/setup.rs b/src/setup.rs index e2b3ceac..8ab51839 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -23,12 +23,18 @@ use std::{io, process::exit}; use tokio_core::reactor::Handle; use tokio_signal::ctrl_c; +// We need to allow these lints since the conditional compilation is +// what makes them appear. +#[allow(unused_must_use, unreachable_code, unused_variables)] pub(crate) fn initial_state( handle: Handle, config: config::SpotifydConfig, ) -> main_loop::MainLoopState { - #[cfg(feature = "alsa_backend")] - let mut mixer = { + // We just need to initialize the mixer. The contents will be mutated for sure + // and therefore we don't need to worry since all of the branches are covered. + let mut mixer: Box Box> = unimplemented!(); + cfg_if::cfg_if! { + if #[cfg(feature = "alsa_backend")] { let local_audio_device = config.audio_device.clone(); let local_control_device = config.control_device.clone(); let local_mixer = config.mixer.clone(); @@ -54,15 +60,12 @@ pub(crate) fn initial_state( linear_scaling: linear, }) as Box }) as Box Box> - } } - }; - - #[cfg(not(feature = "alsa_backend"))] - let mut mixer = { - info!("Using software volume controller."); - Box::new(|| Box::new(mixer::softmixer::SoftMixer::open(None)) as Box) + }} else { + info!("Using software volume controller."); + mixer = Box::new(|| Box::new(mixer::softmixer::SoftMixer::open(None)) as Box) as Box Box> + } }; let cache = config.cache; @@ -138,10 +141,17 @@ pub(crate) fn initial_state( let backend = find_backend(backend.as_ref().map(String::as_ref)); main_loop::MainLoopState { librespot_connection: main_loop::LibreSpotConnection::new(connection, discovery_stream), + #[cfg(feature = "alsa_backend")] + audio_setup: main_loop::AudioSetup { + mixer, + backend, + audio_device: config.audio_device, + }, + #[cfg(not(feature = "alsa_backend"))] audio_setup: main_loop::AudioSetup { mixer, backend, - audio_device: config.audio_device.clone(), + audio_device: None, }, spotifyd_state: main_loop::SpotifydState { ctrl_c_stream: Box::new(ctrl_c(&handle).flatten_stream()),