diff options
author | Toby Vincent <tobyv@tobyvin.dev> | 2024-07-19 17:14:42 -0500 |
---|---|---|
committer | Toby Vincent <tobyv@tobyvin.dev> | 2024-07-19 17:14:42 -0500 |
commit | e12cd9dfb9f20f432edd69414b4ff83325226995 (patch) | |
tree | 986e640f3c6355215d36958476be605819da1c22 /src/bin/title.rs | |
parent | 28bcf144224838e9e93d5926dcdbb20a4d45a8bf (diff) |
feat: working
Diffstat (limited to 'src/bin/title.rs')
-rw-r--r-- | src/bin/title.rs | 183 |
1 files changed, 95 insertions, 88 deletions
diff --git a/src/bin/title.rs b/src/bin/title.rs index 165e715..3daee26 100644 --- a/src/bin/title.rs +++ b/src/bin/title.rs @@ -1,113 +1,120 @@ -use std::{sync::Arc, time::Duration}; +use std::{collections::HashMap, sync::Arc, time::Duration}; -use futures_util::StreamExt; use i3blocks::{ - color_listener, - dbus::{player::PlayerProxy, playerctld::PlayerctldProxy}, + dbus::player::{PlaybackStatus, PlayerProxy}, i3bar::Block, - player_listener, Error, + Component, Error, Update, }; use tokio::{ sync::{mpsc::Sender, Mutex}, task::{AbortHandle, JoinSet}, }; -use zbus::Connection; +use zbus::zvariant::OwnedValue; const TICK_RATE: Duration = Duration::from_millis(500); #[tokio::main] async fn main() -> Result<(), main_error::MainError> { - let mut join_set = JoinSet::new(); - - let (tx_player, mut rx_player) = tokio::sync::mpsc::channel(128); - let (tx_status, mut rx_status) = tokio::sync::mpsc::channel(128); - let (tx_value, mut rx_value) = tokio::sync::mpsc::channel(128); - - let conn = Connection::session().await?; - let proxy = PlayerctldProxy::builder(&conn).build().await?; - - tokio::spawn(player_listener(tx_player, proxy)); - - let status: Arc<Mutex<Block>> = Default::default(); - let mut rotator: Option<AbortHandle> = None; - - let mut interval = tokio::time::interval(TICK_RATE); - let mut chars = Vec::new(); - - loop { - tokio::select! { - Some(name) = rx_player.recv() => { - join_set.shutdown().await; - if name.is_empty() { - let mut status = status.lock().await; - status.full_text = Default::default(); - } else { - let proxy = PlayerProxy::builder(&conn) - .destination(name)? - .build() - .await?; - join_set.spawn(color_listener(tx_status.clone(), proxy.clone())); - join_set.spawn(value_listener(tx_value.clone(), proxy)); - } - } - Some((color, background)) = rx_status.recv() => { - let mut status = status.lock().await; - status.color = color; - status.background = background; - } - Some(mut value) = rx_value.recv() => { - if let Some(h) = rotator.take() { - h.abort(); - } + i3blocks::run::<Title>(Default::default()) + .await + .map_err(Into::into) +} - value.push(' '); - if value.len() >= 10 { - chars = value.chars().collect::<Vec<char>>(); - let mut status = status.lock().await; - status.full_text = format!("{} ", String::from_iter(chars[0..10].iter())); - } else { - chars = Default::default(); - let mut status = status.lock().await; - status.full_text = value; - } - } - _ = interval.tick(), if !chars.is_empty() => { - let mut status = status.lock().await; - status.full_text = format!("{} ", String::from_iter(chars[0..10].iter())); - chars.rotate_left(1); +pub struct Title; + +impl Component for Title { + const NAME: &'static str = "title"; + type Updater = Self; + type Colorer = PlaybackStatus; + type Handler = (); +} + +impl Update for Title { + type Value = String; + + async fn listen(tx: Sender<Self::Value>, proxy: PlayerProxy<'_>) -> Result<(), Error> { + use futures_util::StreamExt; + + let mut join_set = JoinSet::new(); + let mut rotator = None; + let mut old_title = None; + + Self::handle_metadata( + tx.clone(), + proxy.metadata().await?, + &mut old_title, + &mut rotator, + &mut join_set, + ) + .await?; + + let mut stream = proxy.receive_metadata_changed().await; + while let Some(signal) = stream.next().await { + if let Ok(metadata) = signal.get().await { + Self::handle_metadata( + tx.clone(), + metadata, + &mut old_title, + &mut rotator, + &mut join_set, + ) + .await?; } } + Ok(()) + } + + async fn update(mut value: Self::Value, block: Arc<Mutex<Block>>) -> Result<bool, Error> { + value.push(' '); + let mut block = block.lock().await; + block.full_text = value; - let s = status.lock().await; - s.write_stdout()?; + Ok(true) } } -pub async fn value_listener(tx: Sender<String>, proxy: PlayerProxy<'_>) -> Result<(), Error> { - let mut old_title = if let Some(value) = proxy.metadata().await?.get("xesam:title") { - let title: String = value.try_to_owned()?.try_into()?; - tx.send(title.clone()).await?; - Some(title) - } else { - None - }; - - let mut stream = proxy.receive_metadata_changed().await; - while let Some(signal) = stream.next().await { - if let Ok(value) = signal.get().await { - let Some(owned_value) = value.get("xesam:title") else { - continue; - }; - - let title: String = owned_value.try_to_owned()?.try_into()?; - - if old_title.as_ref().is_some_and(|s| *s == title) { - continue; - } +impl Title { + async fn handle_metadata( + tx: Sender<<<Self as Component>::Updater as Update>::Value>, + metadata: HashMap<String, OwnedValue>, + old_title: &mut Option<String>, + rotator: &mut Option<AbortHandle>, + join_set: &mut JoinSet<Result<(), Error>>, + ) -> Result<(), Error> { + let Some(owned_value) = metadata.get("xesam:title") else { + return Ok(()); + }; + + let title: String = owned_value.try_to_owned()?.try_into()?; + + if old_title.as_ref().is_some_and(|s| *s == title) { + return Ok(()); + } + + if let Some(h) = rotator.take() { + h.abort(); + }; - old_title = Some(title.clone()); + *old_title = Some(title.clone()); + + if title.len() >= 10 { + let mut chars = title.clone().chars().collect::<Vec<char>>(); + let tx = tx.clone(); + + *rotator = Some(join_set.spawn(async move { + let mut interval = tokio::time::interval(TICK_RATE); + loop { + interval.tick().await; + tx.send(format!("{} ", String::from_iter(chars[0..10].iter()))) + .await + .unwrap(); + chars.rotate_left(1); + } + })); + } else { tx.send(title).await?; } + + Ok(()) } - Ok(()) } |