Skip to content

Commit

Permalink
src/plugin: refactor main-loop
Browse files Browse the repository at this point in the history
  • Loading branch information
eNV25 committed Oct 11, 2023
1 parent 4543e1c commit c8153bf
Showing 1 changed file with 68 additions and 71 deletions.
139 changes: 68 additions & 71 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,63 +42,42 @@ pub extern "C" fn mpv_open_cplugin(ctx: *mut mpv_handle) -> c_int {
}
}

#[allow(clippy::too_many_lines)]
macro_rules! data {
($source:expr, bool) => {
data!($source, std::ffi::c_int) != 0
};
($source:expr, String) => {
cstr!(*$source.data.cast()).to_owned()
};
($source:expr, $type:ty) => {
*$source.data.cast::<$type>()
};
}

fn plugin(ctx: Handle, name: &str) -> anyhow::Result<()> {
pub const OBJ_PATH: &str = "/org/mpris/MediaPlayer2";
let path = "/org/mpris/MediaPlayer2";
let pid = process::id();
let connection = ConnectionBuilder::session()?
.name(format!("org.mpris.MediaPlayer2.mpv.instance{pid}"))?
.serve_at(OBJ_PATH, crate::mpris2::Root(ctx))?
.serve_at(OBJ_PATH, crate::mpris2::Player(ctx))?
.serve_at(path, crate::mpris2::Root(ctx))?
.serve_at(path, crate::mpris2::Player(ctx))?
.build()?;
let object_server = connection.object_server();
let root = object_server.interface::<_, mpris2::Root>(OBJ_PATH)?;
let player = object_server.interface::<_, mpris2::Player>(OBJ_PATH)?;
let connection = SignalContext::from_parts(connection.into_inner(), path.try_into()?);

// These properties and those handled in the main loop must be kept in sync with those
// mentioned in the interface implementations.
// It's a bit of a pain in the ass but there's no other way.
observe!(ctx, "media-title", "metadata", "duration");
observe!(
ctx,
MPV_FORMAT_FLAG,
"fullscreen",
"seekable",
"idle-active",
"eof-reached",
"pause",
"shuffle",
);
observe!(
ctx,
MPV_FORMAT_STRING,
"keep-open",
"loop-file",
"loop-playlist",
);
observe!(ctx, MPV_FORMAT_DOUBLE, "speed", "volume");
observe_properties(ctx);

let elog = |err| eprintln!("[{name}] {err:?}");

let mut keep_open = false;
let mut seeking = false;
let mut root_changed = HashMap::new();
let mut player_changed = HashMap::new();
let mut root = Vec::new();
let mut player = Vec::new();
loop {
let mut state = scopeguard::guard(
(State::default(), &mut root_changed, &mut player_changed),
|(state, root_changed, player_changed)| {
let root_changed_refs = root_changed.iter().map(|(&k, v)| (k, v)).collect();
let player_changed_refs = player_changed.iter().map(|(&k, v)| (k, v)).collect();
signal_changed(
ctx,
state,
(root.signal_context(), root_changed_refs),
(player.signal_context(), player_changed_refs),
)
.for_each(|err| err.unwrap_or_else(elog));
root_changed.clear();
player_changed.clear();
(State::default(), &mut root, &mut player),
|(state, root, player)| {
signal_changed(ctx, state, &connection, root, player)
.for_each(|err| err.unwrap_or_else(elog));
},
);
let (state, &mut ref mut root_changed, &mut ref mut player_changed) = &mut *state;
Expand All @@ -107,26 +86,14 @@ fn plugin(ctx: Handle, name: &str) -> anyhow::Result<()> {
.chain(iter::repeat(0.0))
.map(|timeout| unsafe { *mpv_wait_event(ctx.into(), timeout) })
{
macro_rules! data {
($source:expr, bool) => {
data!($source, std::ffi::c_int) != 0
};
($source:expr, String) => {
cstr!(*$source.data.cast()).to_owned()
};
($source:expr, $type:ty) => {
*$source.data.cast::<$type>()
};
}
match ev.event_id {
MPV_EVENT_NONE => break,
MPV_EVENT_SHUTDOWN => return Ok(()),
MPV_EVENT_SEEK => seeking = true,
MPV_EVENT_PLAYBACK_RESTART if seeking => {
seeking = false;
if let Ok(position) = get!(ctx, "playback-time", f64) {
seeked(player.signal_context(), mpris2::time_from_secs(position))
.unwrap_or_else(elog);
seeked(&connection, mpris2::time_from_secs(position)).unwrap_or_else(elog);
}
}
MPV_EVENT_PROPERTY_CHANGE => {
Expand All @@ -148,10 +115,10 @@ fn plugin(ctx: Handle, name: &str) -> anyhow::Result<()> {
state.loop_playlist.replace(unsafe { data!(prop, String) });
}
("fullscreen", MPV_FORMAT_FLAG) => {
root_changed.insert("Fullscreen", unsafe { data!(prop, bool) }.into());
root_changed.push(("Fullscreen", unsafe { data!(prop, bool) }.into()));
}
("seekable", MPV_FORMAT_FLAG) => {
player_changed.insert("CanSeek", unsafe { data!(prop, bool) }.into());
player_changed.push(("CanSeek", unsafe { data!(prop, bool) }.into()));
}
("idle-active", MPV_FORMAT_FLAG) => {
state.idle_active.replace(unsafe { data!(prop, bool) });
Expand All @@ -163,14 +130,14 @@ fn plugin(ctx: Handle, name: &str) -> anyhow::Result<()> {
state.pause.replace(unsafe { data!(prop, bool) });
}
("shuffle", MPV_FORMAT_FLAG) => {
player_changed.insert("Shuffle", unsafe { data!(prop, bool) }.into());
player_changed.push(("Shuffle", unsafe { data!(prop, bool) }.into()));
}
("speed", MPV_FORMAT_DOUBLE) => {
player_changed.insert("Rate", unsafe { data!(prop, f64) }.into());
player_changed.push(("Rate", unsafe { data!(prop, f64) }.into()));
}
("volume", MPV_FORMAT_DOUBLE) => {
player_changed
.insert("Volume", (unsafe { data!(prop, f64) } / 100.0).into());
.push(("Volume", (unsafe { data!(prop, f64) } / 100.0).into()));
}
_ => {}
}
Expand All @@ -181,6 +148,31 @@ fn plugin(ctx: Handle, name: &str) -> anyhow::Result<()> {
}
}

// These properties and those handled in the main loop must be kept in sync with those
// mentioned in the interface implementations.
// It's a bit of a pain in the ass but there's no other way.
fn observe_properties(ctx: Handle) {
observe!(ctx, "media-title", "metadata", "duration");
observe!(
ctx,
MPV_FORMAT_FLAG,
"fullscreen",
"seekable",
"idle-active",
"eof-reached",
"pause",
"shuffle",
);
observe!(
ctx,
MPV_FORMAT_STRING,
"keep-open",
"loop-file",
"loop-playlist",
);
observe!(ctx, MPV_FORMAT_DOUBLE, "speed", "volume");
}

#[derive(Clone, Default)]
struct State {
idle_active: Option<bool>,
Expand Down Expand Up @@ -233,24 +225,29 @@ impl State {
fn signal_changed(
ctx: Handle,
mut state: State,
(root_ctxt, root_changed): (&SignalContext<'_>, HashMap<&'static str, &Value<'static>>),
(player_ctxt, mut player_changed): (&SignalContext<'_>, HashMap<&'static str, &Value<'static>>),
ctxt: &SignalContext<'_>,
root: &mut Vec<(&str, Value<'_>)>,
player: &mut Vec<(&str, Value<'_>)>,
) -> impl Iterator<Item = zbus::Result<()>> {
let playback_status = state.playback_status(ctx);
let loop_status = state.loop_status(ctx);
let metadata = state.metadata(ctx);

let (root, player) = (root.drain(..), player.drain(..));
let root: HashMap<_, _> = root.as_slice().iter().map(|(k, v)| (*k, v)).collect();
let mut player: HashMap<_, _> = player.as_slice().iter().map(|(k, v)| (*k, v)).collect();
if let Some(ref value) = playback_status {
player_changed.insert("PlaybackStatus", value);
player.insert("PlaybackStatus", value);
}
if let Some(ref value) = loop_status {
player_changed.insert("LoopStatus", value);
player.insert("LoopStatus", value);
}
if let Some(ref value) = metadata {
player_changed.insert("Metadata", value);
player.insert("Metadata", value);
}
let root = (!root_changed.is_empty())
.then(|| properties_changed(root_ctxt, mpris2::Root::name(), &root_changed));
let player = (!player_changed.is_empty())
.then(|| properties_changed(player_ctxt, mpris2::Player::name(), &player_changed));

let root = (!root.is_empty()).then(|| properties_changed(ctxt, mpris2::Root::name(), &root));
let player =
(!player.is_empty()).then(|| properties_changed(ctxt, mpris2::Player::name(), &player));
root.into_iter().chain(player)
}

0 comments on commit c8153bf

Please sign in to comment.