diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 7c769b63bc29f16b70b9fe28303e447aa6a50eb0..b16e87cffbf60211823ad6b863e5e536bdcda04a 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -3318,6 +3318,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", + "tracing", "windows-sys 0.42.0", ] @@ -4190,6 +4191,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", + "lazy_static", "nix 0.25.1", "once_cell", "ordered-stream", @@ -4198,6 +4200,7 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", + "tokio", "tracing", "uds_windows", "winapi", diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 41cd93d93214cddc52d96397a989548c93aeaae5..b3e941283ed509941489cedff45742651dae107c 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -33,7 +33,10 @@ lazy_static = "^1" thiserror = "^1" # DBus interactions -zbus = { version = "^3" } +zbus = { version = "^3", default-features = false, features = [ + "tokio", + "async-io", +] } # Data Structures hashbrown = { version = "^0.13", features = ["serde"] } diff --git a/src/rust/mpris/src/lib.rs b/src/rust/mpris/src/lib.rs index d28c42369f2242ca0e4243259e9cf249c369b5d6..e728031c4d2f100f9c09dee0da006b0a47714aee 100644 --- a/src/rust/mpris/src/lib.rs +++ b/src/rust/mpris/src/lib.rs @@ -1,2 +1,2 @@ -pub mod traits; +pub mod server; pub mod types; diff --git a/src/rust/mpris/src/server.rs b/src/rust/mpris/src/server.rs new file mode 100644 index 0000000000000000000000000000000000000000..993d31ca74d9caf7d7da0f5679bcf66582c30901 --- /dev/null +++ b/src/rust/mpris/src/server.rs @@ -0,0 +1,184 @@ +//! # DBus interface for: `org.mpris.MediaPlayer2`, `org.mpris.MediaPlayer2.Player` + +// use crate::types::*; +use zbus::dbus_interface; + +pub struct MediaPlayer2 { + identity: String, +} + +#[dbus_interface(name = "org.mpris.MediaPlayer2")] +impl MediaPlayer2 { + /// Quit method + fn quit(&self) {} + + /// Raise method + fn raise(&self) {} + + /// CanQuit property + #[dbus_interface(property, name = "CanQuit")] + fn can_quit(&self) -> bool { + false + } + + /// CanRaise property + #[dbus_interface(property, name = "CanRaise")] + fn can_raise(&self) -> bool { + false + } + + /// CanSetFullscreen property + #[dbus_interface(property, name = "CanSetFullscreen")] + fn can_set_fullscreen(&self) -> bool { + false + } + + /// DesktopEntry property + #[dbus_interface(property, name = "DesktopEntry")] + fn desktop_entry(&self) -> String { + todo!() + } + + /// Fullscreen property + #[dbus_interface(property, name = "Fullscreen")] + fn fullscreen(&self) -> bool { + false + } + #[dbus_interface(property, name = "Fullscreen")] + fn set_fullscreen(&mut self, _value: bool) {} + + /// HasTrackList property + #[dbus_interface(property, name = "HasTrackList")] + fn has_track_list(&self) -> bool { + false + } + + /// Identity property + #[dbus_interface(property, name = "Identity")] + fn identity(&self) -> String { + self.identity.clone() + } + + /// SupportedMimeTypes property + #[dbus_interface(property, name = "SupportedMimeTypes")] + fn supported_mime_types(&self) -> Vec<String> { + todo!() + } + + /// SupportedUriSchemes property. It should be `id://` and `file://` for + /// lektord. Here we support `file://` for only files in the database to + /// enable the user to search the kara folder and drop the files to play + /// them imediatly. + #[dbus_interface(property, name = "SupportedUriSchemes")] + fn supported_uri_schemes(&self) -> Vec<String> { + vec![String::from("id://"), String::from("file://")] + } +} + +// #[dbus_interface( +// interface = "org.mpris.MediaPlayer2.Player", +// default_path = "/org/mpris/MediaPlayer2", +// assume_defaults = true +// )] +// trait Player { +// /// Next method +// fn next(&self) -> zbus::Result<()>; +// +// /// OpenUri method +// fn open_uri(&self, id_uri: &str) -> zbus::Result<()>; +// +// /// Pause method +// fn pause(&self) -> zbus::Result<()>; +// +// /// Play method +// fn play(&self) -> zbus::Result<()>; +// +// /// PlayPause method +// fn play_pause(&self) -> zbus::Result<()>; +// +// /// Previous method +// fn previous(&self) -> zbus::Result<()>; +// +// /// Seek method +// fn seek(&self, time: TimeMicroSec) -> zbus::Result<()>; +// +// /// SetPosition method +// fn set_position( +// &self, +// track_id: &MediaPlayerObjectPath, +// time: TimeMicroSec, +// ) -> zbus::Result<()>; +// +// /// Stop method +// fn stop(&self) -> zbus::Result<()>; +// +// /// CanControl property +// #[dbus_interface(property)] +// fn can_control(&self) -> zbus::Result<bool>; +// +// /// CanPause property +// #[dbus_interface(property)] +// fn can_pause(&self) -> zbus::Result<bool>; +// +// /// CanPlay property +// #[dbus_interface(property)] +// fn can_play(&self) -> zbus::Result<bool>; +// +// /// CanSeek property +// #[dbus_interface(property)] +// fn can_seek(&self) -> zbus::Result<bool>; +// +// /// LoopStatus property +// #[dbus_interface(property)] +// fn loop_status(&self) -> zbus::Result<MediaPlayerObjectPath>; +// fn set_loop_status(&self, value: &MediaPlayerObjectPath) -> zbus::Result<()>; +// +// /// MaximumRate property. We will only set it to `1.0` here... +// #[dbus_interface(property)] +// fn maximum_rate(&self) -> zbus::Result<f64> { +// Ok(1.0) +// } +// fn set_maximum_rate(&self, _: f64) -> zbus::Result<()> { +// Ok(()) +// } +// +// /// Metadata property. If there is a current kara playing, there must be at +// /// least a `mpris:trackid` value of type `o` which is the object path of +// /// the current kara id. +// #[dbus_interface(property)] +// fn metadata( +// &self, +// ) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>; +// +// /// MinimumRate property. We will only set it to `1.0` here... +// #[dbus_interface(property)] +// fn minimum_rate(&self) -> zbus::Result<f64> { +// Ok(1.0) +// } +// fn set_minimum_rate(&self, _: f64) -> zbus::Result<()> { +// Ok(()) +// } +// +// /// PlaybackStatus property +// #[dbus_interface(property)] +// fn playback_status(&self) -> zbus::Result<MediaPlayerPlaybackStatus>; +// +// /// Position property +// #[dbus_interface(property)] +// fn position(&self) -> zbus::Result<TimeMicroSec>; +// +// /// Rate property +// #[dbus_interface(property)] +// fn rate(&self) -> zbus::Result<f64>; +// fn set_rate(&self, value: f64) -> zbus::Result<()>; +// +// /// Shuffle property +// #[dbus_interface(property)] +// fn shuffle(&self) -> zbus::Result<bool>; +// fn set_shuffle(&self, value: bool) -> zbus::Result<()>; +// +// /// Volume property +// #[dbus_interface(property)] +// fn volume(&self) -> zbus::Result<f64>; +// fn set_volume(&self, value: f64) -> zbus::Result<()>; +// } diff --git a/src/rust/mpris/src/traits.rs b/src/rust/mpris/src/traits.rs deleted file mode 100644 index e964c95393727d50d820e5dcb0ee3124d04110ef..0000000000000000000000000000000000000000 --- a/src/rust/mpris/src/traits.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! # DBus interface proxies for: `org.mpris.MediaPlayer2`, `org.mpris.MediaPlayer2.Player` -//! -//! This code was generated by `zbus-xmlgen` `3.1.0` from DBus introspection data. -//! Source: `mpris.xml`. -//! -//! You may prefer to adapt it, instead of using it verbatim. -//! -//! More information can be found in the -//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) -//! section of the zbus documentation. - -use crate::types::*; -use zbus::dbus_proxy; - -#[dbus_proxy(interface = "org.mpris.MediaPlayer2", assume_defaults = true)] -trait MediaPlayer2 { - /// Quit method - fn quit(&self) -> zbus::Result<()>; - - /// Raise method - fn raise(&self) -> zbus::Result<()>; - - /// CanQuit property - #[dbus_proxy(property)] - fn can_quit(&self) -> zbus::Result<bool>; - - /// CanRaise property - #[dbus_proxy(property)] - fn can_raise(&self) -> zbus::Result<bool>; - - /// CanSetFullscreen property - #[dbus_proxy(property)] - fn can_set_fullscreen(&self) -> zbus::Result<bool>; - - /// DesktopEntry property - #[dbus_proxy(property)] - fn desktop_entry(&self) -> zbus::Result<String>; - - /// Fullscreen property - #[dbus_proxy(property)] - fn fullscreen(&self) -> zbus::Result<bool>; - fn set_fullscreen(&self, value: bool) -> zbus::Result<()>; - - /// HasTrackList property - #[dbus_proxy(property)] - fn has_track_list(&self) -> zbus::Result<bool>; - - /// Identity property - #[dbus_proxy(property)] - fn identity(&self) -> zbus::Result<String>; - - /// SupportedMimeTypes property - #[dbus_proxy(property)] - fn supported_mime_types(&self) -> zbus::Result<Vec<String>>; - - /// SupportedUriSchemes property. It should be `id://` and `file://` for - /// lektord. Here we support `file://` for only files in the database to - /// enable the user to search the kara folder and drop the files to play - /// them imediatly. - #[dbus_proxy(property)] - fn supported_uri_schemes(&self) -> zbus::Result<Vec<String>>; -} - -#[dbus_proxy(interface = "org.mpris.MediaPlayer2.Player", assume_defaults = true)] -trait Player { - /// Next method - fn next(&self) -> zbus::Result<()>; - - /// OpenUri method - fn open_uri(&self, id_uri: &str) -> zbus::Result<()>; - - /// Pause method - fn pause(&self) -> zbus::Result<()>; - - /// Play method - fn play(&self) -> zbus::Result<()>; - - /// PlayPause method - fn play_pause(&self) -> zbus::Result<()>; - - /// Previous method - fn previous(&self) -> zbus::Result<()>; - - /// Seek method - fn seek(&self, time: TimeMicroSec) -> zbus::Result<()>; - - /// SetPosition method - fn set_position( - &self, - track_id: &MediaPlayerObjectPath, - time: TimeMicroSec, - ) -> zbus::Result<()>; - - /// Stop method - fn stop(&self) -> zbus::Result<()>; - - /// CanControl property - #[dbus_proxy(property)] - fn can_control(&self) -> zbus::Result<bool>; - - /// CanPause property - #[dbus_proxy(property)] - fn can_pause(&self) -> zbus::Result<bool>; - - /// CanPlay property - #[dbus_proxy(property)] - fn can_play(&self) -> zbus::Result<bool>; - - /// CanSeek property - #[dbus_proxy(property)] - fn can_seek(&self) -> zbus::Result<bool>; - - /// LoopStatus property - #[dbus_proxy(property)] - fn loop_status(&self) -> zbus::Result<MediaPlayerObjectPath>; - fn set_loop_status(&self, value: &MediaPlayerObjectPath) -> zbus::Result<()>; - - /// MaximumRate property. We will only set it to `1.0` here... - #[dbus_proxy(property)] - fn maximum_rate(&self) -> zbus::Result<f64> { - Ok(1.0) - } - fn set_maximum_rate(&self, _: f64) -> zbus::Result<()> { - Ok(()) - } - - /// Metadata property. If there is a current kara playing, there must be at - /// least a `mpris:trackid` value of type `o` which is the object path of - /// the current kara id. - #[dbus_proxy(property)] - fn metadata( - &self, - ) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>; - - /// MinimumRate property. We will only set it to `1.0` here... - #[dbus_proxy(property)] - fn minimum_rate(&self) -> zbus::Result<f64> { - Ok(1.0) - } - fn set_minimum_rate(&self, _: f64) -> zbus::Result<()> { - Ok(()) - } - - /// PlaybackStatus property - #[dbus_proxy(property)] - fn playback_status(&self) -> zbus::Result<MediaPlayerPlaybackStatus>; - - /// Position property - #[dbus_proxy(property)] - fn position(&self) -> zbus::Result<TimeMicroSec>; - - /// Rate property - #[dbus_proxy(property)] - fn rate(&self) -> zbus::Result<f64>; - fn set_rate(&self, value: f64) -> zbus::Result<()>; - - /// Shuffle property - #[dbus_proxy(property)] - fn shuffle(&self) -> zbus::Result<bool>; - fn set_shuffle(&self, value: bool) -> zbus::Result<()>; - - /// Volume property - #[dbus_proxy(property)] - fn volume(&self) -> zbus::Result<f64>; - fn set_volume(&self, value: f64) -> zbus::Result<()>; -} diff --git a/src/rust/mpris/src/types.rs b/src/rust/mpris/src/types.rs index 9d0067e44468427f5d594b923c9c6fae0fb1b6db..07af4a2bff2d05ee867c000f43f7f7b898af73f7 100644 --- a/src/rust/mpris/src/types.rs +++ b/src/rust/mpris/src/types.rs @@ -1,10 +1,9 @@ use serde::{Deserialize, Serialize}; -use zbus::zvariant::{ - ObjectPath as ZObjectPath, OwnedValue as ZOwnedValue, Signature as ZSignature, Type as ZType, -}; +use zbus::zvariant::{ObjectPath as ZObjectPath, OwnedValue as ZOwnedValue, Type as ZType}; /// The loop status. -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, ZType, Clone, Copy, PartialEq, Eq)] +#[zvariant(signature = "s")] pub enum MediaPlayerLoopStatus { None, Track, @@ -12,7 +11,8 @@ pub enum MediaPlayerLoopStatus { } /// The playback status. -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, ZType, Clone, Copy, PartialEq, Eq)] +#[zvariant(signature = "s")] pub enum MediaPlayerPlaybackStatus { Playing, Paused, @@ -20,20 +20,27 @@ pub enum MediaPlayerPlaybackStatus { } /// Force the time to be in µs. -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, ZType, Clone, Copy, PartialEq, Eq)] #[serde(transparent)] +#[zvariant(signature = "x")] pub struct TimeMicroSec(i64); -/// A valid path must be prefiexed by `/baka/lektor/`. After that we have the -/// variant name in lower case, then a `/`, then the ascii only value. For -/// example we can have: -/// - `/baka/lektor/id/42` -#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)] +/// A valid path must be prefiexed by `/org/mpris/MediaPlayer2`. After that we +/// have the variant name, then a `/`, then the ascii only value. For example +/// we can have: +/// - `/org/mpris/MediaPlayer2/Id/42` +/// - `/org/mpris/MediaPlayer2/None` +/// - `/org/mpris/MediaPlayer2/TrackList/NoTrack` is a special case... +#[derive(Debug, Serialize, Deserialize, ZType, Clone, Copy, PartialEq, Eq, Default)] +#[zvariant(signature = "o")] pub enum MediaPlayerObjectPath { /// Not an object, or nothing. #[default] None, + /// There is no track. + NoTrack, + /// An id. Id(i64), } @@ -82,32 +89,12 @@ impl<'a> From<ZOwnedValue> for MediaPlayerObjectPath { let value: ZObjectPath = value.try_into().unwrap_or_default(); let value = value.trim_start_matches('/'); match value.split('/').collect::<Vec<_>>()[..] { - ["baka", "lektor", "id", id] => id.parse::<i64>().map(Self::Id).unwrap_or_default(), + ["org", "mpris", "MediaPlayer2", "TrackList", "NoTrack"] => Self::NoTrack, + ["org", "mpris", "MediaPlayer2", "None"] => Self::None, + ["org", "mpris", "MediaPlayer2", "Id", id] => { + id.parse::<i64>().map(Self::Id).unwrap_or_default() + } _ => Default::default(), } } } - -impl ZType for MediaPlayerObjectPath { - fn signature() -> ZSignature<'static> { - ZSignature::try_from("o").expect("invalid signature") - } -} - -impl ZType for MediaPlayerLoopStatus { - fn signature() -> ZSignature<'static> { - ZSignature::try_from("s").expect("invalid signature") - } -} - -impl ZType for MediaPlayerPlaybackStatus { - fn signature() -> ZSignature<'static> { - ZSignature::try_from("s").expect("invalid signature") - } -} - -impl ZType for TimeMicroSec { - fn signature() -> ZSignature<'static> { - ZSignature::try_from("x").expect("invalid signature") - } -}