1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use crate::Result as DefaultResult;
use dbus::{
channel::Token,
message::{MatchRule, Message},
nonblock::{MsgMatch, SyncConnection},
strings::{Member, Path},
};
use std::error::Error;
/// Enum for indicating which type of MPRIS event to listen
/// for.
pub enum EventType {
/// Emitted whenever properties change.
/// A list of all properties that will
/// cause this to be emitted can be found
/// [here].
///
/// [here]: https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Property:PlaybackStatus
PropertiesChanged,
/// Emitted whenever the active track is seeked.
Seeked,
}
/// A struct that simplifies the process of adding
/// and removing listeners and callbacks to/from MPRIS
/// `DBus` signals.
pub struct EventManager<'a> {
conn: &'a SyncConnection,
callback_tokens: Vec<Token>,
}
impl EventManager<'_> {
/// Creates a new event manager.
pub fn new(conn: &SyncConnection) -> EventManager {
EventManager {
conn,
callback_tokens: Vec::new(),
}
}
/// Adds a new callback to the event manager.
///
/// Callbacks can be provided either as a closure, or as
/// a function. A callback takes only one parameter, a
/// [`Message`](crate::Message).
///
/// # Errors
/// Returns an `Err` if there is a failure in adding
/// a match rule to the connection.
///
/// # Example
/// ```
/// let mut manager = EventManager::new(&connection);
/// // Be advised that it is important that this is assigned to a variable
/// let _incoming = manager
/// .add_callback(EventType::PropertiesChanged, |msg| {
/// println!("Data: {:?}", msg);
/// true
/// })
/// .await?;
/// ```
pub async fn add_callback<F>(
&mut self,
event_type: EventType,
mut callback: F,
) -> Result<MsgMatch, Box<dyn Error>>
where
F: FnMut(Message) -> bool + Send + 'static,
{
let mut rule = MatchRule::new();
rule.member = Some(Member::new(match event_type {
EventType::PropertiesChanged => "PropertiesChanged",
EventType::Seeked => "Seeked",
})?);
rule.path = Some(Path::new("/org/mpris/MediaPlayer2")?);
let msg_match = self.conn.add_match(rule).await?;
let registered_callback = msg_match.cb(move |msg, _: ()| callback(msg));
self.callback_tokens.push(registered_callback.token());
Ok(registered_callback)
}
/// Clears all registered callbacks from the manager.
///
/// # Errors
/// Returns an `Err` if there is a failure in removing
/// a match from the connection.
pub async fn clear_callbacks(&mut self) -> DefaultResult<()> {
for token in &self.callback_tokens {
self.conn.remove_match(*token).await?;
}
Ok(())
}
}