From f960ed0f47a56f0dad0d94aadcd08aa2a3c481fc Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Sat, 3 Aug 2024 20:08:31 -0500 Subject: wip: more on wrapper --- src/dbus/player.rs | 8 ++-- src/i3bar.rs | 20 ++++++++++ src/listener.rs | 115 ++++++++++++++++++++++++++++------------------------- src/main.rs | 31 ++++++++++----- 4 files changed, 106 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/dbus/player.rs b/src/dbus/player.rs index a78d7ed..b3424a4 100644 --- a/src/dbus/player.rs +++ b/src/dbus/player.rs @@ -176,7 +176,7 @@ pub struct Metadata { /// Integer: The speed of the music, in beats per minute. #[zvariant(rename = "xesam:audioBPM")] - pub audio_bpm: Option, + pub audio_bpm: Option, /// Float: An automatically-generated rating, based on things such as how often it has been played. This should be in the range 0.0 to 1.0. #[zvariant(rename = "xesam:autoRating")] @@ -196,7 +196,7 @@ pub struct Metadata { /// Integer: The disc number on the album that this track is from. #[zvariant(rename = "xesam:discNumber")] - pub disc_number: Option, + pub disc_number: Option, /// Date/Time: When the track was first played. #[zvariant(rename = "xesam:firstUsed")] @@ -220,7 +220,7 @@ pub struct Metadata { /// Integer: The track number on the album disc. #[zvariant(rename = "xesam:trackNumber")] - pub track_number: Option, + pub track_number: Option, /// URI: The location of the media file. #[zvariant(rename = "xesam:url")] @@ -228,7 +228,7 @@ pub struct Metadata { /// Integer: The number of times the track has been played. #[zvariant(rename = "xesam:useCount")] - pub use_count: Option, + pub use_count: Option, /// Float: A user-specified rating. This should be in the range 0.0 to 1.0. #[zvariant(rename = "xesam:userRating")] diff --git a/src/i3bar.rs b/src/i3bar.rs index b69095e..6d74693 100644 --- a/src/i3bar.rs +++ b/src/i3bar.rs @@ -2,6 +2,26 @@ use serde::{Deserialize, Serialize}; use crate::Error; +/// Represent header as described in +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Header { + version: u8, + stop_signal: u16, + cont_signal: u16, + click_events: bool, +} + +impl Default for Header { + fn default() -> Self { + Self { + version: 1, + stop_signal: 10, + cont_signal: 12, + click_events: true, + } + } +} + /// Represent block as described in #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Block { diff --git a/src/listener.rs b/src/listener.rs index 88efb6c..5f86d18 100644 --- a/src/listener.rs +++ b/src/listener.rs @@ -1,8 +1,7 @@ use std::{ops::ControlFlow, sync::LazyLock, time::Duration}; -use tokio::task::JoinHandle; use tokio_stream::StreamExt; -use zbus::{proxy::PropertyChanged, Connection}; +use zbus::{names::UniqueName, proxy::PropertyChanged, Connection}; use crate::{ dbus::player::{Metadata, PlaybackStatus, PlayerProxy}, @@ -22,80 +21,88 @@ static COLORS: LazyLock<[String; 3]> = LazyLock::new(|| { }); pub async fn listeners(conn: Connection, blocks: State) -> Result<(), Error> { - let proxy = PlayerProxy::builder(&conn) - .destination("org.mpris.MediaPlayer2.playerctld")? - .build() - .await?; - - let mut rotator: Option>> = None; - let mut old_title = String::new(); - - let mut metadata = proxy.receive_metadata_changed().await; - let mut prev = proxy.receive_can_go_previous_changed().await; - let mut play = proxy.receive_playback_status_changed().await; - let mut next = proxy.receive_can_go_next_changed().await; - let mut volume = proxy.receive_volume_changed().await; - loop { - tokio::select! { - Some(prop) = metadata.next() => handle_metadata(prop, blocks.clone(), &mut old_title, &mut rotator).await, - Some(prop) = prev.next() => handle_prev_next(prop, blocks.clone(), BlockKind::Prev).await, - Some(prop) = play.next() => handle_play(prop, blocks.clone()).await, - Some(prop) = next.next() => handle_prev_next(prop, blocks.clone(), BlockKind::Next).await, - Some(prop) = volume.next() => handle_volume(prop, blocks.clone()).await, - else => { - eprintln!("Failed to get next property"); - break + let player_proxy = PlayerProxy::builder(&conn) + .destination("org.mpris.MediaPlayer2.playerctld")? + .build() + .await?; + + //let mut rotator: Option>> = None; + let mut old_title = String::new(); + let mut chars: Vec = Vec::new(); + + let mut owner_changed = player_proxy.inner().receive_owner_changed().await?; + let mut metadata = player_proxy.receive_metadata_changed().await; + let mut prev = player_proxy.receive_can_go_previous_changed().await; + let mut play = player_proxy.receive_playback_status_changed().await; + let mut next = player_proxy.receive_can_go_next_changed().await; + let mut volume = player_proxy.receive_volume_changed().await; + + loop { + let control_flow = tokio::select! { + Some(prop) = owner_changed.next() => handle_owner_changed(prop).await, + Some(prop) = metadata.next() => handle_metadata(prop, blocks.clone(), &mut old_title, &mut chars).await, + Some(prop) = prev.next() => handle_prev_next(prop, blocks.clone(), BlockKind::Prev).await, + Some(prop) = play.next() => handle_play(prop, blocks.clone()).await, + Some(prop) = next.next() => handle_prev_next(prop, blocks.clone(), BlockKind::Next).await, + Some(prop) = volume.next() => handle_volume(prop, blocks.clone()).await, + _ = tokio::time::sleep(TICK_RATE), if chars.len() > 10 => { + chars.rotate_left(1); + handle_title(&chars, blocks.clone()).await + } + }; + + match control_flow { + ControlFlow::Continue(_) => blocks.notify.notify_one(), + ControlFlow::Break(res) => { + res?; + break; + } } - - }; - - blocks.notify.notify_one(); + } } - - Ok(()) } -async fn title_rotator(blocks: State, title: String) -> Result<(), Error> { - let mut interval = tokio::time::interval(TICK_RATE); - let mut chars = title.chars().collect::>(); - chars.push(' '); - let full_text = String::from_iter(chars[0..10].iter()); +async fn handle_owner_changed(prop: Option>) -> ControlFlow> { + if prop.is_none() { + ControlFlow::Break(Ok(())) + } else { + ControlFlow::Continue(()) + } +} +async fn handle_title(title: &[char], blocks: State) -> ControlFlow> { let block = &mut blocks.value.write().await[BlockKind::Title as usize]; - block.min_width = Some(MinWidth::Text(full_text.len() + 1)); - block.full_text = full_text; - block.enabled = false; + block.full_text = String::from_iter(title.iter().take(10)); + block.min_width = Some(MinWidth::Text(block.full_text.len() + 1)); + block.enabled = true; - loop { - interval.tick().await; - let block = &mut blocks.value.write().await[BlockKind::Title as usize]; - block.full_text = String::from_iter(chars[0..10].iter()); - chars.rotate_left(1); - } + ControlFlow::Continue(()) } async fn handle_metadata( prop: PropertyChanged<'_, Metadata>, blocks: State, old_title: &mut String, - rotator: &mut Option>>, + chars: &mut Vec, ) -> ControlFlow> { - let title = match prop.get().await.map(|m| m.title) { + let title = match prop + .get() + .await + .map(|m| m.title) + .inspect_err(|err| eprintln!("{err}")) + { Ok(Some(s)) if *old_title != s => s, _ => return ControlFlow::Continue(()), }; old_title.clone_from(&title); - rotator.take().into_iter().for_each(|h| h.abort()); + + *chars = title.chars().collect::>(); + handle_title(chars, blocks).await; if title.len() >= 10 { - *rotator = Some(tokio::spawn(title_rotator(blocks.clone(), title))); - } else { - let block = &mut blocks.value.write().await[BlockKind::Title as usize]; - block.min_width = Some(MinWidth::Text(title.len() + 1)); - block.full_text = title; - block.enabled = true; + chars.push(' '); } ControlFlow::Continue(()) diff --git a/src/main.rs b/src/main.rs index cd17ec2..146d450 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::{process::Stdio, sync::Arc}; use i3blocks_mpris::{ handler::handlers, - i3bar::{Align, Block, Click, MinWidth}, + i3bar::{Align, Block, Click, Header, MinWidth}, listener::listeners, BlockKind, Watcher, }; @@ -95,39 +95,50 @@ async fn main() -> Result<(), main_error::MainError> { tokio::spawn(listeners(conn.clone(), blocks.clone())); tokio::spawn(handlers(conn, rx)); + let mut header = serde_json::to_string(&Header::default())?; + header.push_str("\n[[]\n"); + stdout.write_all(header.as_bytes()).await?; + stdout.flush().await?; + let mut mpris_blocks = String::new(); let mut other_blocks = String::new(); loop { tokio::select! { _ = blocks.notify.notified() => { - mpris_blocks = blocks + let enabled = blocks .value .read() .await .iter() .filter(|b| b.enabled) .map(serde_json::to_string) - .collect::, _>>()? - .join(","); + .collect::, _>>()?; + mpris_blocks = if enabled.is_empty() { + r#"{"name":"mpris","full_text":""}"#.to_owned() + } else { + enabled.join(",") + }; } - Ok(Some(line)) = child_stdout.next_line() => other_blocks = line, + Ok(Some(line)) = child_stdout.next_line() => { + if line.starts_with(",[{") { + other_blocks = line + } + }, Ok(Some(line)) = stdin.next_line() => { handle_stdin(&line, tx.clone(), &mut child_stdin).await?; continue; } - else => break, + else => continue, } - let output = other_blocks.replace(r#"{"name":"mpris","full_text":""},"#, &mpris_blocks); - if !output.is_empty() { + if !other_blocks.is_empty() { + let output = other_blocks.replace(r#"{"name":"mpris","full_text":""}"#, &mpris_blocks); stdout.write_all(output.as_bytes()).await?; stdout.write_u8(b'\n').await?; stdout.flush().await?; } } - - Ok(()) } async fn handle_stdin( -- cgit v1.2.3-70-g09d2