Merge pull request #1 from pop-os/more-bindings

Add more zbus bindings
This commit is contained in:
Jeremy Soller 2023-02-01 09:47:32 -07:00 committed by GitHub
commit dd3d4935a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 3421 additions and 1 deletions

View file

@ -1,2 +1,6 @@
[workspace]
members = ["networkmanager"]
members = [
"networkmanager",
"timedate",
"upower",
]

2
mpris2/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

17
mpris2/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "mpris2-zbus"
description = "zbus-based bindings for MPRIS2 (Media Player Remote Interfacing Specification) on Linux"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
time = { version = "0.3", features = ["parsing"] }
zbus = "3.8.0"
zvariant = "3.10"
[dev-dependencies]
miette = { version = "4.3", features = ["fancy"] }
tokio = { version = "1", features = ["full"] }

346
mpris2/LICENSE.md Normal file
View file

@ -0,0 +1,346 @@
# Mozilla Public License Version 2.0
### 1. Definitions
**1.1. “Contributor”**
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
**1.2. “Contributor Version”**
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
**1.3. “Contribution”**
means Covered Software of a particular Contributor.
**1.4. “Covered Software”**
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
**1.5. “Incompatible With Secondary Licenses”**
means
- **(a)** that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
- **(b)** that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
**1.6. “Executable Form”**
means any form of the work other than Source Code Form.
**1.7. “Larger Work”**
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
**1.8. “License”**
means this document.
**1.9. “Licensable”**
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
**1.10. “Modifications”**
means any of the following:
- **(a)** any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
- **(b)** any new file in Source Code Form that contains any Covered
Software.
**1.11. “Patent Claims” of a Contributor**
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
**1.12. “Secondary License”**
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
**1.13. “Source Code Form”**
means the form of the work preferred for making modifications.
**1.14. “You” (or “Your”)**
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, “control” means **(a)** the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or **(b)** ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
### 2. License Grants and Conditions
#### 2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
- **(a)** under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
- **(b)** under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
#### 2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
#### 2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
- **(a)** for any code that a Contributor has removed from Covered Software;
or
- **(b)** for infringements caused by: **(i)** Your and any other third party's
modifications of Covered Software, or **(ii)** the combination of its
Contributions with other software (except as part of its Contributor
Version); or
- **(c)** under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
#### 2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
#### 2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
#### 2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
#### 2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
### 3. Responsibilities
#### 3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
#### 3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
- **(a)** such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
- **(b)** You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
#### 3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
#### 3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
#### 3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
### 4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: **(a)** comply with
the terms of this License to the maximum extent possible; and **(b)**
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
### 5. Termination
**5.1.** The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated **(a)** provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and **(b)** on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
**5.2.** If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
### 6. Disclaimer of Warranty
> Covered Software is provided under this License on an “as is”
> basis, without warranty of any kind, either expressed, implied, or
> statutory, including, without limitation, warranties that the
> Covered Software is free of defects, merchantable, fit for a
> particular purpose or non-infringing. The entire risk as to the
> quality and performance of the Covered Software is with You.
> Should any Covered Software prove defective in any respect, You
> (not any Contributor) assume the cost of any necessary servicing,
> repair, or correction. This disclaimer of warranty constitutes an
> essential part of this License. No use of any Covered Software is
> authorized under this License except under this disclaimer.
### 7. Limitation of Liability
> Under no circumstances and under no legal theory, whether tort
> (including negligence), contract, or otherwise, shall any
> Contributor, or anyone who distributes Covered Software as
> permitted above, be liable to You for any direct, indirect,
> special, incidental, or consequential damages of any character
> including, without limitation, damages for lost profits, loss of
> goodwill, work stoppage, computer failure or malfunction, or any
> and all other commercial damages or losses, even if such party
> shall have been informed of the possibility of such damages. This
> limitation of liability shall not apply to liability for death or
> personal injury resulting from such party's negligence to the
> extent applicable law prohibits such limitation. Some
> jurisdictions do not allow the exclusion or limitation of
> incidental or consequential damages, so this exclusion and
> limitation may not apply to You.
### 8. Litigation
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
### 9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
### 10. Versions of the License
#### 10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
#### 10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
#### 10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
## Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
## Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

3
mpris2/README.md Normal file
View file

@ -0,0 +1,3 @@
# mpris2-zbus
A zbus client proxy for [org.mpris.MediaPlayer2](https://mpris2.readthedocs.io/en/latest/).

156
mpris2/examples/list.rs Normal file
View file

@ -0,0 +1,156 @@
// SPDX-License-Identifier: MPL-2.0
use miette::{IntoDiagnostic, Result, WrapErr};
use mpris2_zbus::{media_player::MediaPlayer, playlists::ordering::PlaylistOrdering};
use zbus::Connection;
#[tokio::main]
async fn main() -> Result<()> {
let connection = Connection::session()
.await
.into_diagnostic()
.wrap_err("Failed to establish session D-Bus connection")?;
let media_players = MediaPlayer::new_all(&connection)
.await
.into_diagnostic()
.wrap_err("Failed get available players")?;
for media_player in media_players {
let name = media_player
.identity()
.await
.into_diagnostic()
.wrap_err("Failed to get identity for media player")?;
let desktop_entry = media_player
.desktop_entry()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get desktop entry for media player '{}'", name))?;
println!("{} ({})", name, desktop_entry);
let player = media_player
.player()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get player for media player '{}'", name))?;
let playback_status = player
.playback_status()
.await
.into_diagnostic()
.wrap_err_with(|| {
format!("Failed to get playback status for media player '{}'", name)
})?;
println!("\tPlayback Status: {}", playback_status);
let position = player
.position()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get position for media player '{}'", name))?
.map(|s| format!("{} seconds", s.as_seconds_f32()))
.unwrap_or_else(|| "N/A".to_owned());
println!("\tPosition: {}", position);
if !player
.can_seek()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get can_seek for media player '{}'", name))?
{
println!("\tDoesn't support seeking");
} else {
println!("\tSupports seeking");
}
let supported_rates = player
.available_rates()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get supported rates for media player '{}'", name))?
.map(|s| format!("{}x through {}x", s.start(), s.end()))
.unwrap_or_else(|| "N/A".to_owned());
println!("\tSupported Rates: {}", supported_rates);
let current_rate = player
.rate()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get current rate for media player '{}'", name))?
.map(|s| format!("{}x", s))
.unwrap_or_else(|| "N/A".to_owned());
println!("\tCurrent Rate: {}", current_rate);
let metadata = player
.metadata()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get metadata for media player '{}'", name))?;
println!("\tMetadata: {}", metadata);
let track_list = media_player
.track_list()
.await
.into_diagnostic()
.wrap_err_with(|| format!("Failed to get track list for media player '{}'", name))?;
if let Some(track_list) = track_list {
let tracks = track_list
.detailed_tracks()
.await
.into_diagnostic()
.wrap_err_with(|| {
format!("Failed to get detailed tracks for media player '{}'", name)
})?;
println!("\tTracks:");
for (track, metadata) in tracks {
println!("\t\t{}", track);
if let Some(title) = metadata.title() {
println!("\t\t\tTitle: {}", title);
}
if let Some(album) = metadata.album() {
println!("\t\t\tAlbum: {}", album);
}
if let Some(artists) = metadata.artists() {
println!("\t\t\tArtists: {}", artists.join(", "));
}
if let Some(composers) = metadata.composer() {
println!("\t\t\tComposers: {}", composers.join(", "));
}
if let Some(bpm) = metadata.bpm() {
println!("\t\t\tBPM: {}", bpm);
}
}
} else {
println!("\tTracks: N/A");
}
let playlists = media_player
.playlists()
.await
.into_diagnostic()
.wrap_err_with(|| {
format!(
"Failed to get playlists interface for media player '{}'",
name
)
})?;
if let Some(playlists) = playlists {
println!("\tPlaylists:");
let playlist_count = playlists
.playlist_count()
.await
.into_diagnostic()
.wrap_err_with(|| {
format!("Failed to get playlist count for media player '{}'", name)
})?;
if playlist_count > 0 {
let playlists = playlists
.get_playlists(0, playlist_count, PlaylistOrdering::Alphabetical, false)
.await
.into_diagnostic()
.wrap_err_with(|| {
format!("Failed to get playlists for media player '{}'", name)
})?;
for playlist in playlists {
println!("\t{}", playlist.id());
println!("\t\tName: {}", playlist.name());
println!("\t\tIcon: {}", playlist.icon());
}
} else {
println!("\t\t None :(");
}
} else {
println!("\tPlaylists: N/A");
}
}
Ok(())
}

5
mpris2/src/bindings.rs Normal file
View file

@ -0,0 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
pub mod media_player;
pub mod player;
pub mod playlist;
pub mod track_list;

View file

@ -0,0 +1,63 @@
// SPDX-License-Identifier: MPL-2.0
//! # DBus interface proxies for: `org.mpris.MediaPlayer2`
//!
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! Source: `Interface '/org/mpris/MediaPlayer2' from service 'org.mpris.MediaPlayer2.firefox.instance103520' on session bus`.
//!
//! 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.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PropertiesProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PeerProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.mpris.MediaPlayer2",
default_path = "/org/mpris/MediaPlayer2"
)]
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>;
/// DesktopEntry property
#[dbus_proxy(property)]
fn desktop_entry(&self) -> zbus::Result<String>;
/// 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
#[dbus_proxy(property)]
fn supported_uri_schemes(&self) -> zbus::Result<Vec<String>>;
}

View file

@ -0,0 +1,131 @@
// SPDX-License-Identifier: MPL-2.0
//! # DBus interface proxies for: `org.mpris.MediaPlayer2.Player`
//!
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! Source: `Interface '/org/mpris/MediaPlayer2' from service 'org.mpris.MediaPlayer2.firefox.instance103520' on session bus`.
//!
//! 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.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PropertiesProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PeerProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use crate::track::TrackId;
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.mpris.MediaPlayer2.Player",
default_path = "/org/mpris/MediaPlayer2"
)]
trait Player {
/// Next method
fn next(&self) -> zbus::Result<()>;
/// OpenUri method
fn open_uri(&self, 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, offset: i64) -> zbus::Result<()>;
/// SetPosition method
fn set_position(&self, track_id: &TrackId, position: i64) -> zbus::Result<()>;
/// Stop method
fn stop(&self) -> zbus::Result<()>;
/// Seeked signal
#[dbus_proxy(signal)]
fn seeked(&self, position: i64) -> zbus::Result<()>;
/// CanControl property
#[dbus_proxy(property)]
fn can_control(&self) -> zbus::Result<bool>;
/// CanGoNext property
#[dbus_proxy(property)]
fn can_go_next(&self) -> zbus::Result<bool>;
/// CanGoPrevious property
#[dbus_proxy(property)]
fn can_go_previous(&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>;
/// MaximumRate property
#[dbus_proxy(property)]
fn maximum_rate(&self) -> zbus::Result<f64>;
/// Metadata property
#[dbus_proxy(property)]
fn metadata(
&self,
) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
/// MinimumRate property
#[dbus_proxy(property)]
fn minimum_rate(&self) -> zbus::Result<f64>;
/// PlaybackStatus property
#[dbus_proxy(property)]
fn playback_status(&self) -> zbus::Result<String>;
/// Position property
#[dbus_proxy(property)]
fn position(&self) -> zbus::Result<i64>;
/// Rate property
#[dbus_proxy(property)]
fn rate(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn set_rate(&self, value: f64) -> zbus::Result<()>;
/// Shuffle property (optional)
#[dbus_proxy(property)]
fn shuffle(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn set_shuffle(&self, value: bool) -> zbus::Result<()>;
/// LoopStatus property (optional)
#[dbus_proxy(property)]
fn loop_status(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn set_loop_status(&self, value: String) -> zbus::Result<()>;
/// Volume property
#[dbus_proxy(property)]
fn volume(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn set_volume(&self, value: f64) -> zbus::Result<()>;
}

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MPL-2.0
//! # DBus interface proxies for: `org.mpris.MediaPlayer2`, `org.mpris.MediaPlayer2.Player`, `org.mpris.MediaPlayer2.TrackList`, `org.mpris.MediaPlayer2.Playlists`
//!
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! Source: `Interface '/org/mpris/MediaPlayer2' from service 'org.mpris.MediaPlayer2.org.gnome.Music' on session bus`.
//!
//! 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.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use crate::playlists::{id::PlaylistId, ordering::PlaylistOrdering, playlist::Playlist};
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.mpris.MediaPlayer2.Playlists",
default_path = "/org/mpris/MediaPlayer2"
)]
trait Playlists {
/// ActivatePlaylist method
fn activate_playlist(&self, playlist_id: &PlaylistId) -> zbus::Result<()>;
/// GetPlaylists method
fn get_playlists(
&self,
index: u32,
max_count: u32,
order: PlaylistOrdering,
reverse_order: bool,
) -> zbus::Result<Vec<Playlist>>;
/// PlaylistChanged signal
#[dbus_proxy(signal)]
fn playlist_changed(&self, playlist: Playlist) -> zbus::Result<()>;
/// ActivePlaylist property
#[dbus_proxy(property)]
fn active_playlist(&self) -> zbus::Result<(bool, Playlist)>;
/// Orderings property
#[dbus_proxy(property)]
fn orderings(&self) -> zbus::Result<Vec<String>>;
/// PlaylistCount property
#[dbus_proxy(property)]
fn playlist_count(&self) -> zbus::Result<u32>;
}

View file

@ -0,0 +1,58 @@
// SPDX-License-Identifier: MPL-2.0
//! # DBus interface proxies for: `org.mpris.MediaPlayer2`, `org.mpris.MediaPlayer2.Player`, `org.mpris.MediaPlayer2.TrackList`, `org.mpris.MediaPlayer2.Playlists`
//!
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! Source: `Interface '/org/mpris/MediaPlayer2' from service 'org.mpris.MediaPlayer2.org.gnome.Music' on session bus`.
//!
//! 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.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use crate::track::TrackId;
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.mpris.MediaPlayer2.TrackList",
default_path = "/org/mpris/MediaPlayer2"
)]
trait TrackList {
/// AddTrack method
fn add_track(&self, uri: &str, after_track: &TrackId, set_as_current: bool)
-> zbus::Result<()>;
/// GetTracksMetadata method
fn get_tracks_metadata(
&self,
track_ids: Vec<TrackId>,
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
/// GoTo method
fn go_to(&self, track_id: &TrackId) -> zbus::Result<()>;
/// RemoveTrack method
fn remove_track(&self, track_id: &TrackId) -> zbus::Result<()>;
/// TrackListReplaced signal
#[dbus_proxy(signal)]
fn track_list_replaced(&self, tracks: Vec<TrackId>, current_track: TrackId)
-> zbus::Result<()>;
/// CanEditTracks property
#[dbus_proxy(property)]
fn can_edit_tracks(&self) -> zbus::Result<bool>;
/// Tracks property
#[dbus_proxy(property)]
fn tracks(&self) -> zbus::Result<Vec<TrackId>>;
}

51
mpris2/src/error.rs Normal file
View file

@ -0,0 +1,51 @@
// SPDX-License-Identifier: MPL-2.0
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Invalid enum variant when converting from String.
#[error("Invalid enum variant: {got}, expected something in {expected:?}")]
InvalidEnum {
got: String,
expected: &'static [&'static str],
},
#[error("Tried to extract a {wanted}, but it was actually {actual}")]
IncorrectVariant {
wanted: &'static str,
actual: &'static str,
},
#[error("Tried to convert Value::{wanted}, but it was got {actual:?}")]
IncorrectValue {
wanted: &'static str,
actual: zvariant::OwnedValue,
},
/// A zbus error.
#[error("zbus error: {0}")]
Zbus(zbus::Error),
/// A zbus::fdo error.
#[error("zbus fdo error: {0}")]
Fdo(zbus::fdo::Error),
}
impl From<zbus::fdo::Error> for Error {
fn from(err: zbus::fdo::Error) -> Self {
match err {
zbus::fdo::Error::ZBus(err) => Self::Zbus(err),
_ => Self::Fdo(err),
}
}
}
impl From<zbus::Error> for Error {
fn from(err: zbus::Error) -> Self {
match err {
zbus::Error::FDO(err) => Self::Fdo(*err),
_ => Self::Zbus(err),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;

21
mpris2/src/lib.rs Normal file
View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
pub mod bindings;
pub mod error;
pub mod media_player;
pub mod metadata;
pub mod player;
pub mod playlists;
pub mod track;
pub mod track_list;
pub(crate) fn handle_optional<T>(input: zbus::Result<T>) -> error::Result<Option<T>> {
match input {
Ok(input) => Ok(Some(input)),
Err(zbus::Error::FDO(fdo_error))
if matches!(*fdo_error, zbus::fdo::Error::NotSupported(_)) =>
{
Ok(None)
}
Err(err) => Err(error::Error::from(err)),
}
}

111
mpris2/src/media_player.rs Normal file
View file

@ -0,0 +1,111 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
bindings::{
media_player::MediaPlayer2Proxy, player::PlayerProxy, playlist::PlaylistsProxy,
track_list::TrackListProxy,
},
error::{Error, Result},
player::Player,
playlists::Playlists,
track_list::TrackList,
};
use std::ops::Deref;
use zbus::{fdo::DBusProxy, names::OwnedBusName, Connection};
#[derive(Debug, Clone)]
pub struct MediaPlayer {
proxy: MediaPlayer2Proxy<'static>,
}
impl MediaPlayer {
/// Creates a new instance of the `org.mpris.MediaPlayer2` interface.
pub async fn new(connection: &Connection, name: OwnedBusName) -> Result<Self> {
MediaPlayer2Proxy::builder(connection)
.destination(name)?
.build()
.await
.map(Self::from)
.map_err(Error::from)
}
/// Gets the names of all the MPRIS players that are available on the current session.
pub async fn available_players(connection: &Connection) -> Result<Vec<OwnedBusName>> {
let dbus = DBusProxy::builder(connection)
.path("/org/freedesktop/DBus")?
.build()
.await?;
let mut players = Vec::new();
for name in dbus.list_names().await? {
if name.starts_with("org.mpris.MediaPlayer2.") {
players.push(name);
}
}
Ok(players)
}
/// Gets a new instance of all the MPRIS players that are available on the current session.
pub async fn new_all(connection: &Connection) -> Result<Vec<Self>> {
let players = Self::available_players(connection).await?;
let mut instances = Vec::with_capacity(players.len());
for player in players {
instances.push(Self::new(connection, player).await?);
}
Ok(instances)
}
/// Returns an instance to the `org.mpris.MediaPlayer2.Player` interface of this object.
pub async fn player(&self) -> Result<Player> {
PlayerProxy::builder(self.proxy.connection())
.destination(self.proxy.destination().to_owned())?
.build()
.await
.map(Player::from)
.map_err(Error::from)
}
/// Returns an instance to the `org.mpris.MediaPlayer2.TrackList` interface of this object,
/// if a track list is available.
pub async fn track_list(&self) -> Result<Option<TrackList>> {
if self.proxy.has_track_list().await? {
TrackListProxy::builder(self.proxy.connection())
.destination(self.proxy.destination().to_owned())?
.build()
.await
.map(TrackList::from)
.map(Some)
.map_err(Error::from)
} else {
Ok(None)
}
}
/// Returns an instance to the `org.mpris.MediaPlayer2.Playlists` interface of this object,
/// if a track list is available.
pub async fn playlists(&self) -> Result<Option<Playlists>> {
if self.proxy.has_track_list().await? {
PlaylistsProxy::builder(self.proxy.connection())
.destination(self.proxy.destination().to_owned())?
.build()
.await
.map(Playlists::from)
.map(Some)
.map_err(Error::from)
} else {
Ok(None)
}
}
}
impl Deref for MediaPlayer {
type Target = MediaPlayer2Proxy<'static>;
fn deref(&self) -> &Self::Target {
&self.proxy
}
}
impl From<MediaPlayer2Proxy<'static>> for MediaPlayer {
fn from(proxy: MediaPlayer2Proxy<'static>) -> Self {
Self { proxy }
}
}

533
mpris2/src/metadata.rs Normal file
View file

@ -0,0 +1,533 @@
// SPDX-License-Identifier: MPL-2.0
use crate::error::{Error, Result};
use std::{
collections::HashMap,
fmt,
ops::{Deref, DerefMut},
};
use time::{Duration, OffsetDateTime};
use zbus::zvariant::{OwnedObjectPath, Value as ZValue};
#[derive(Debug, Clone, PartialEq)]
pub struct Metadata {
inner: HashMap<String, MetadataValue>,
}
impl Metadata {
/// `xesam:album`: The track artist(s).
pub fn album(&self) -> Option<String> {
self.inner
.get("xesam:album")
.cloned()
.and_then(|v| v.try_into_string().ok())
}
/// `xesam:artist`: The track artist(s).
pub fn artists(&self) -> Option<Vec<String>> {
self.inner
.get("xesam:artist")
.cloned()
.and_then(|artists| artists.try_into_array().ok())
.map(|artists| {
artists
.into_iter()
.filter_map(|v| v.try_into_string().ok())
.collect()
})
}
/// `xesam:asText`: The track lyrics.
pub fn lyrics(&self) -> Option<String> {
self.inner
.get("xesam:asText")
.cloned()
.and_then(|v| v.try_into_string().ok())
}
/// `xesam:albumArtist`: The album artist(s).
pub fn album_artists(&self) -> Option<Vec<String>> {
self.inner
.get("xesam:albumArtist")
.cloned()
.and_then(|artists| artists.try_into_array().ok())
.map(|artists| {
artists
.into_iter()
.filter_map(|v| v.try_into_string().ok())
.collect()
})
}
/// `xesam:audioBPM`: The speed of the music, in beats per minute.
pub fn bpm(&self) -> Option<u64> {
self.inner
.get("xesam:audioBPM")
.cloned()
.and_then(|v| v.try_into_uint().ok())
}
/// `xesam:autoRating`: 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.
pub fn auto_rating(&self) -> Option<f64> {
self.inner
.get("xesam:autoRating")
.cloned()
.and_then(|v| v.try_into_double().ok())
}
/// `xesam:composer`: The composer(s) of the track.
pub fn composer(&self) -> Option<Vec<String>> {
self.inner
.get("xesam:composer")
.cloned()
.and_then(|artists| artists.try_into_array().ok())
.map(|artists| {
artists
.into_iter()
.filter_map(|v| v.try_into_string().ok())
.collect()
})
}
/// `xesam:contentCreated`: When the track was created. Usually only the year component will be useful.
pub fn created(&self) -> Option<OffsetDateTime> {
self.inner
.get("xesam:contentCreated")
.cloned()
.and_then(|v| v.try_into_date().ok())
}
/// `xesam:discNumber`: The disc number on the album that this track is from.
pub fn disc_number(&self) -> Option<u64> {
self.inner
.get("xesam:discNumber")
.cloned()
.and_then(|v| v.try_into_uint().ok())
}
/// `xesam:firstUsed`: When the track was first played.
pub fn first_played(&self) -> Option<OffsetDateTime> {
self.inner
.get("xesam:firstUsed")
.cloned()
.and_then(|v| v.try_into_date().ok())
}
/// `xesam:genre`: The genre(s) of the track.
pub fn genre(&self) -> Option<Vec<String>> {
self.inner
.get("xesam:genre")
.cloned()
.and_then(|artists| artists.try_into_array().ok())
.map(|artists| {
artists
.into_iter()
.filter_map(|v| v.try_into_string().ok())
.collect()
})
}
/// `xesam:lastUsed`: When the track was last played.
pub fn last_played(&self) -> Option<OffsetDateTime> {
self.inner
.get("xesam:lastUsed")
.cloned()
.and_then(|v| v.try_into_date().ok())
}
/// `xesam:lyricist`: The lyricist(s) of the track.
pub fn lyricist(&self) -> Option<Vec<String>> {
self.inner
.get("xesam:lyricist")
.cloned()
.and_then(|artists| artists.try_into_array().ok())
.map(|artists| {
artists
.into_iter()
.filter_map(|v| v.try_into_string().ok())
.collect()
})
}
/// `xesam:title`: The track title.
pub fn title(&self) -> Option<String> {
self.inner
.get("xesam:title")
.cloned()
.and_then(|v| v.try_into_string().ok())
}
/// `xesam:trackNumber`: The track number on the album that this track is from.
pub fn track_number(&self) -> Option<u64> {
self.inner
.get("xesam:trackNumber")
.cloned()
.and_then(|v| v.try_into_uint().ok())
}
/// `xesam:url`: The location of the media file.
pub fn url(&self) -> Option<String> {
self.inner
.get("xesam:url")
.cloned()
.and_then(|v| v.try_into_string().ok())
}
/// `xesam:useCount`: The number of times the track has been played.
pub fn use_count(&self) -> Option<u64> {
self.inner
.get("xesam:useCount")
.cloned()
.and_then(|v| v.try_into_uint().ok())
}
/// `xesam:userRating`: The user's rating of the track.
pub fn user_rating(&self) -> Option<f64> {
self.inner
.get("xesam:userRating")
.cloned()
.and_then(|v| v.try_into_double().ok())
}
/// `mpris:trackid`: D-Bus path: A unique identity for this track within the context of an MPRIS object (eg: tracklist).
pub fn track_id(&self) -> Option<OwnedObjectPath> {
self.inner
.get("mpris:trackid")
.cloned()
.and_then(|v| v.try_into_string().ok())
.and_then(|path| OwnedObjectPath::try_from(path).ok())
}
/// `mpris:length`: The length of the track in microseconds.
pub fn length(&self) -> Option<Duration> {
self.inner
.get("mpris:length")
.cloned()
.and_then(|v| match &v {
MetadataValue::Int(i) => Some(*i),
MetadataValue::UInt(u) => Some(*u as i64),
MetadataValue::Str(s) => s.parse().ok(),
_ => None,
})
.map(Duration::microseconds)
}
/// `mpris:artUrl`: The location of an image representing the track or album.
/// Clients should not assume this will continue to exist when the media player stops giving out the URL.
pub fn art_url(&self) -> Option<String> {
self.inner
.get("mpris:artUrl")
.cloned()
.and_then(|v| v.try_into_string().ok())
}
}
impl Deref for Metadata {
type Target = HashMap<String, MetadataValue>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Metadata {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl fmt::Display for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
let mut iter = self.inner.iter().peekable();
while let Some((k, v)) = iter.next() {
if iter.peek().is_some() {
write!(f, "{}: {}, ", k, v)?;
} else {
write!(f, "{}: {}", k, v)?;
}
}
write!(f, "}}")
}
}
impl<'a, V: Into<ZValue<'a>>> From<HashMap<String, V>> for Metadata {
fn from(map: HashMap<String, V>) -> Self {
Self {
inner: map
.into_iter()
.map(|(k, v)| (k, MetadataValue::from(&v.into())))
.collect(),
}
}
}
#[derive(Clone, PartialEq)]
pub enum MetadataValue {
Str(String),
Double(f64),
Int(i64),
UInt(u64),
Bool(bool),
Array(Vec<MetadataValue>),
Dict(HashMap<String, MetadataValue>),
__Unsupported,
}
impl MetadataValue {
fn variant(&self) -> &'static str {
match self {
MetadataValue::Str(_) => "Str",
MetadataValue::Double(_) => "Double",
MetadataValue::Int(_) => "Int",
MetadataValue::UInt(_) => "UInt",
MetadataValue::Bool(_) => "Bool",
MetadataValue::Array(_) => "Array",
MetadataValue::Dict(_) => "Dict",
MetadataValue::__Unsupported => "Unsupported",
}
}
/// Tries to extract a string from the variant,
/// returning an error if the variant is not a string.
pub fn try_into_string(self) -> Result<String> {
match self {
MetadataValue::Str(s) => Ok(s),
_ => Err(Error::IncorrectVariant {
wanted: "Str",
actual: self.variant(),
}),
}
}
/// Tries to extract a string from the variant,
/// panicking if the variant is not a string.
pub fn into_string(self) -> String {
self.try_into_string()
.unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract a date/time from the variant,
/// returning an error if the variant is not a date/time.
pub fn try_into_date(self) -> Result<OffsetDateTime> {
let variant = self.variant();
match self {
MetadataValue::Str(s) => {
OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339).map_err(
|_| Error::IncorrectVariant {
wanted: "String (DateTime)",
actual: variant,
},
)
}
_ => Err(Error::IncorrectVariant {
wanted: "String (DateTime)",
actual: variant,
}),
}
}
/// Tries to extract a date/time from the variant,
/// panicking if the variant is not a date/time.
pub fn into_date(self) -> OffsetDateTime {
self.try_into_date().unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract a double from the variant,
/// returning an error if the variant is not a double.
pub fn try_into_double(self) -> Result<f64> {
match self {
MetadataValue::Double(d) => Ok(d),
_ => Err(Error::IncorrectVariant {
wanted: "Double",
actual: self.variant(),
}),
}
}
/// Tries to extract a double from the variant,
/// panicking if the variant is not a double.
pub fn into_double(self) -> f64 {
self.try_into_double()
.unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract an integer from the variant,
/// returning an error if the variant is not an integer.
pub fn try_into_int(self) -> Result<i64> {
match self {
MetadataValue::Int(i) => Ok(i),
_ => Err(Error::IncorrectVariant {
wanted: "Int",
actual: self.variant(),
}),
}
}
/// Tries to extract an integer from the variant,
/// panicking if the variant is not an integer.
pub fn into_int(self) -> i64 {
self.try_into_int().unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract an unsigned integer from the variant,
/// returning an error if the variant is not an unsigned integer.
pub fn try_into_uint(self) -> Result<u64> {
match self {
MetadataValue::UInt(u) => Ok(u),
_ => Err(Error::IncorrectVariant {
wanted: "UInt",
actual: self.variant(),
}),
}
}
/// Tries to extract an unsigned integer from the variant,
/// panicking if the variant is not an unsigned integer.
pub fn into_uint(self) -> u64 {
self.try_into_uint().unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract a boolean from the variant,
/// returning an error if the variant is not a boolean.
pub fn try_into_bool(self) -> Result<bool> {
match self {
MetadataValue::Bool(b) => Ok(b),
_ => Err(Error::IncorrectVariant {
wanted: "Bool",
actual: self.variant(),
}),
}
}
/// Tries to extract a boolean from the variant,
/// panicking if the variant is not a boolean.
pub fn into_bool(self) -> bool {
self.try_into_bool().unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract an array from the variant,
/// returning an error if the variant is not an array.
pub fn try_into_array(self) -> Result<Vec<MetadataValue>> {
match self {
MetadataValue::Array(a) => Ok(a),
_ => Err(Error::IncorrectVariant {
wanted: "Array",
actual: self.variant(),
}),
}
}
/// Tries to extract an array from the variant,
/// panicking if the variant is not an array.
pub fn into_array(self) -> Vec<MetadataValue> {
self.try_into_array()
.unwrap_or_else(|err| panic!("{}", err))
}
/// Tries to extract a dictionary from the variant,
/// returning an error if the variant is not a dictionary.
/// The dictionary is returned as a map from string keys to values.
pub fn try_into_dict(self) -> Result<HashMap<String, MetadataValue>> {
match self {
MetadataValue::Dict(d) => Ok(d),
_ => Err(Error::IncorrectVariant {
wanted: "Dict",
actual: self.variant(),
}),
}
}
/// Tries to extract a dictionary from the variant,
/// panicking if the variant is not a dictionary.
/// The dictionary is returned as a map from string keys to values.
pub fn into_dict(self) -> HashMap<String, MetadataValue> {
self.try_into_dict().unwrap_or_else(|err| panic!("{}", err))
}
}
impl<'a> From<&ZValue<'a>> for MetadataValue {
fn from(value: &ZValue) -> Self {
match value {
ZValue::U8(u) => Self::UInt(*u as u64),
ZValue::Bool(b) => Self::Bool(*b),
ZValue::I16(i) => Self::Int(*i as i64),
ZValue::U16(u) => Self::UInt(*u as u64),
ZValue::I32(i) => Self::Int(*i as i64),
ZValue::U32(u) => Self::UInt(*u as u64),
ZValue::I64(i) => Self::Int(*i),
ZValue::U64(u) => Self::UInt(*u),
ZValue::F64(f) => Self::Double(*f),
ZValue::Str(s) => Self::Str(s.to_string()),
ZValue::ObjectPath(path) => Self::Str(path.to_string()),
ZValue::Array(a) => Self::Array(a.iter().map(|v| v.into()).collect()),
ZValue::Dict(d) => Self::Dict(
HashMap::<String, ZValue>::try_from(d.to_owned())
.unwrap()
.into_iter()
.map(|(k, v)| (k, (&v).into()))
.collect(),
),
ZValue::Value(value) => Self::from(&**value),
_ => Self::__Unsupported,
}
}
}
impl fmt::Debug for MetadataValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::__Unsupported => write!(f, "__Unsupported"),
Self::Int(i) => write!(f, "{}", i),
Self::UInt(u) => write!(f, "{}", u),
Self::Double(d) => write!(f, "{}", d),
Self::Str(s) => write!(f, "{}", s),
Self::Bool(b) => write!(f, "{}", b),
Self::Array(a) => write!(f, "{:?}", a),
Self::Dict(d) => {
let mut debug_struct = f.debug_struct("Dict");
for (k, v) in d {
debug_struct.field(k, &v);
}
debug_struct.finish()
}
}
}
}
impl fmt::Display for MetadataValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::__Unsupported => write!(f, "__Unsupported"),
Self::Int(i) => write!(f, "{}", i),
Self::UInt(u) => write!(f, "{}", u),
Self::Double(d) => write!(f, "{}", d),
Self::Str(s) => write!(f, "\"{}\"", s),
Self::Bool(b) => write!(f, "{}", b),
Self::Array(a) => {
write!(f, "[")?;
let mut iter = a.iter().peekable();
while let Some(value) = iter.next() {
if iter.peek().is_some() {
write!(f, "{}, ", value)?;
} else {
write!(f, "{}", value)?;
}
}
write!(f, "]")
}
Self::Dict(d) => {
write!(f, "{{")?;
let mut iter = d.iter().peekable();
while let Some((k, v)) = iter.next() {
if iter.peek().is_some() {
write!(f, "{}: {}, ", k, v)?;
} else {
write!(f, "{}: {}", k, v)?;
}
}
write!(f, "}}")
}
}
}
}

267
mpris2/src/player.rs Normal file
View file

@ -0,0 +1,267 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
bindings::{media_player::MediaPlayer2Proxy, player::PlayerProxy},
error::{Error, Result},
handle_optional,
media_player::MediaPlayer,
metadata::Metadata,
track::TrackId,
};
use std::{
fmt::{self, Display},
ops::Deref,
str::FromStr,
};
use time::Duration;
use zbus::{names::OwnedBusName, Connection};
#[derive(Debug, Clone)]
pub struct Player {
proxy: PlayerProxy<'static>,
}
impl Player {
/// Creates a new instance of the `org.mpris.MediaPlayer2.Player` interface.
pub async fn new(connection: &Connection, name: OwnedBusName) -> Result<Self> {
PlayerProxy::builder(connection)
.destination(name)?
.build()
.await
.map(Self::from)
.map_err(Error::from)
}
/// Returns this player's `org.mpris.MediaPlayer2` instance
pub async fn media_player(&self) -> Result<MediaPlayer> {
let proxy = MediaPlayer2Proxy::builder(self.proxy.connection())
.destination(self.proxy.destination().to_owned())?
.build()
.await?;
Ok(proxy.into())
}
/// Seeks the specified duration.
pub async fn seek(&self, duration: Duration) -> Result<bool> {
if self.proxy.can_seek().await? {
self.proxy
.seek(duration.whole_microseconds() as i64)
.await?;
Ok(true)
} else {
Ok(false)
}
}
/// Sets the current track position.
///
/// If `track` does not match the id of the currently-playing track, the call is ignored as "stale".
pub async fn set_position(&self, track: &TrackId, position: Duration) -> Result<()> {
self.proxy
.set_position(track, position.whole_microseconds() as i64)
.await
.map_err(Error::from)
}
/// How far into the current track the player is.
///
/// Not all players support this, and it will return None if this is the case.
pub async fn position(&self) -> Result<Option<Duration>> {
handle_optional(self.proxy.position().await.map(Duration::microseconds))
}
/// Gets the current playback status of the player.
pub async fn playback_status(&self) -> Result<PlaybackStatus> {
self.proxy
.playback_status()
.await
.map_err(Error::from)
.and_then(|status| PlaybackStatus::from_str(&status))
}
/// Returns the current rate of playback.
///
/// Not all players support this, and it will return None if this is the case.
pub async fn rate(&self) -> Result<Option<f64>> {
handle_optional(self.proxy.rate().await)
}
/// Sets the current rate of playback.
pub async fn set_rate(&self, value: f64) -> Result<()> {
handle_optional(self.proxy.set_rate(value).await).map(|_| ())
}
/// Returns the minimum supported rate for the player.
///
/// Not all players support this, and it will return None if this is the case.
pub async fn minimum_rate(&self) -> Result<Option<f64>> {
handle_optional(self.proxy.minimum_rate().await)
}
/// Returns the minimum supported rate for the player.
///
/// Not all players support this, and it will return None if this is the case.
pub async fn maximum_rate(&self) -> Result<Option<f64>> {
handle_optional(self.proxy.maximum_rate().await)
}
/// Returns the range of playback rates available for the player.
///
/// Not all players support this, and it will return None if this is the case.
pub async fn available_rates(&self) -> Result<Option<std::ops::RangeInclusive<f64>>> {
let minimum = match self.minimum_rate().await? {
Some(min) => min,
None => return Ok(None),
};
let maximum = match self.maximum_rate().await? {
Some(max) => max,
None => return Ok(None),
};
Ok(Some(minimum..=maximum))
}
/// Returns the metadata for the player.
pub async fn metadata(&self) -> Result<Metadata> {
self.proxy
.metadata()
.await
.map(|metadata| metadata.into())
.map_err(Error::from)
}
/// Whether the current playlist is shuffled or not.
///
/// A value of false indicates that playback is progressing linearly through a playlist,
/// while true means playback is progressing through a playlist in some other order.
pub async fn shuffle(&self) -> Result<Option<bool>> {
if self.can_control().await? {
handle_optional(self.proxy.shuffle().await)
} else {
Ok(None)
}
}
/// Set whether the current playlist is shuffled or not.
///
/// A value of false indicates that playback is progressing linearly through a playlist,
/// while true means playback is progressing through a playlist in some other order.
pub async fn set_shuffle(&self, value: bool) -> Result<()> {
if self.proxy.can_control().await? {
self.proxy.set_shuffle(value).await.map_err(Error::from)
} else {
Ok(())
}
}
/// The current loop / repeat status.
pub async fn loop_status(&self) -> Result<Option<LoopStatus>> {
if self.proxy.can_control().await? {
handle_optional(self.proxy.loop_status().await)
.map(|status| status.and_then(|status| LoopStatus::from_str(&status).ok()))
} else {
Ok(None)
}
}
/// Set the current loop / repeat status.
pub async fn set_loop_status(&self, value: LoopStatus) -> Result<()> {
if self.proxy.can_control().await? {
handle_optional(self.proxy.set_loop_status(value.to_string()).await).map(|_| ())
} else {
Ok(())
}
}
}
impl Deref for Player {
type Target = PlayerProxy<'static>;
fn deref(&self) -> &Self::Target {
&self.proxy
}
}
impl From<PlayerProxy<'static>> for Player {
fn from(proxy: PlayerProxy<'static>) -> Self {
Self { proxy }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PlaybackStatus {
/// A track is currently playing.
Playing,
/// A track is currently paused.
Paused,
/// There is no track currently playing.
Stopped,
}
impl FromStr for PlaybackStatus {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().trim() {
"playing" => Ok(Self::Playing),
"paused" => Ok(Self::Paused),
"stopped" => Ok(Self::Stopped),
_ => Err(Error::InvalidEnum {
got: s.to_string(),
expected: &["Playing", "Paused", "Stopped"],
}),
}
}
}
impl Display for PlaybackStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Playing => "Playing",
Self::Paused => "Paused",
Self::Stopped => "Stopped",
}
)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum LoopStatus {
/// The playback will stop when there are no more tracks to play
None,
/// The current track will start again from the begining once it has finished playing
Track,
/// The playback loops through a list of tracks
Playlist,
}
impl FromStr for LoopStatus {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().trim() {
"none" => Ok(Self::None),
"track" => Ok(Self::Track),
"playlist" => Ok(Self::Playlist),
_ => Err(Error::InvalidEnum {
got: s.to_string(),
expected: &["Playing", "Paused", "Stopped"],
}),
}
}
}
impl Display for LoopStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::None => "None",
Self::Track => "Track",
Self::Playlist => "Playlist",
}
)
}
}

41
mpris2/src/playlists.rs Normal file
View file

@ -0,0 +1,41 @@
// SPDX-License-Identifier: MPL-2.0
pub mod id;
pub mod ordering;
pub mod playlist;
use crate::{
bindings::playlist::PlaylistsProxy,
error::{Error, Result},
};
use std::ops::Deref;
use zbus::{names::OwnedBusName, Connection};
pub struct Playlists {
proxy: PlaylistsProxy<'static>,
}
impl Playlists {
/// Creates a new instance of the `org.mpris.MediaPlayer2.Playlists` interface.
pub async fn new(connection: &Connection, name: OwnedBusName) -> Result<Self> {
PlaylistsProxy::builder(connection)
.destination(name)?
.build()
.await
.map(Self::from)
.map_err(Error::from)
}
}
impl Deref for Playlists {
type Target = PlaylistsProxy<'static>;
fn deref(&self) -> &Self::Target {
&self.proxy
}
}
impl From<PlaylistsProxy<'static>> for Playlists {
fn from(proxy: PlaylistsProxy<'static>) -> Self {
Self { proxy }
}
}

View file

@ -0,0 +1,53 @@
// SPDX-License-Identifier: MPL-2.0
use serde::{Deserialize, Serialize};
use std::{
cmp::{Ord, Ordering, PartialOrd},
fmt::{self, Display},
ops::Deref,
};
use zvariant::{ObjectPath, OwnedObjectPath, Type, Value};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Type, Serialize, Deserialize, Value)]
pub struct PlaylistId(OwnedObjectPath);
impl PlaylistId {
pub fn into_inner(self) -> OwnedObjectPath {
self.0
}
pub fn into_static_path(self) -> ObjectPath<'static> {
self.0.into_inner().into_owned()
}
}
impl Deref for PlaylistId {
type Target = OwnedObjectPath;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> AsRef<ObjectPath<'a>> for PlaylistId {
fn as_ref(&self) -> &ObjectPath<'a> {
&self.0
}
}
impl PartialOrd for PlaylistId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.as_str().partial_cmp(other.0.as_str())
}
}
impl Ord for PlaylistId {
fn cmp(&self, other: &Self) -> Ordering {
self.0.as_str().cmp(other.0.as_str())
}
}
impl Display for PlaylistId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.as_str())
}
}

View file

@ -0,0 +1,120 @@
// SPDX-License-Identifier: MPL-2.0
use crate::error::{Error, Result};
use serde::{
de::{self, Deserialize, Visitor},
ser::{Serialize, Serializer},
};
use std::{
fmt::{self, Display},
str::FromStr,
};
use zvariant::{OwnedValue, Signature, Type, Value};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PlaylistOrdering {
/// Alphabetical ordering by name, ascending.
Alphabetical,
/// Ordering by creation date, oldest first.
CreationDate,
/// Ordering by last modified date, oldest first.
ModifiedDate,
/// Ordering by date of last playback, oldest first.
LastPlayDate,
/// A user-defined ordering.
UserDefined,
}
impl Type for PlaylistOrdering {
fn signature() -> Signature<'static> {
String::signature()
}
}
impl<'a> TryFrom<Value<'a>> for PlaylistOrdering {
type Error = Error;
fn try_from(value: Value<'a>) -> Result<Self> {
match value {
Value::Str(value) => Self::from_str(&value),
_ => Err(Error::IncorrectValue {
wanted: "Str",
actual: OwnedValue::from(value),
}),
}
}
}
impl<'a> From<PlaylistOrdering> for Value<'a> {
fn from(ordering: PlaylistOrdering) -> Self {
Value::Str(ordering.to_string().into())
}
}
impl FromStr for PlaylistOrdering {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().trim() {
"alphabetical" => Ok(Self::Alphabetical),
"created" => Ok(Self::CreationDate),
"modified" => Ok(Self::ModifiedDate),
"played" => Ok(Self::LastPlayDate),
"user" => Ok(Self::UserDefined),
_ => Err(Error::InvalidEnum {
got: s.to_string(),
expected: &["Alphabetical", "Created", "Modified", "Played", "User"],
}),
}
}
}
impl Display for PlaylistOrdering {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Alphabetical => "Alphabetical",
Self::CreationDate => "Created",
Self::ModifiedDate => "Modified",
Self::LastPlayDate => "Played",
Self::UserDefined => "User",
}
)
}
}
impl Serialize for PlaylistOrdering {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}
impl<'de> Deserialize<'de> for PlaylistOrdering {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_str(PlaylistOrderingVisitor)
}
}
struct PlaylistOrderingVisitor;
impl Visitor<'_> for PlaylistOrderingVisitor {
type Value = PlaylistOrdering;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a string")
}
fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
where
E: de::Error,
{
PlaylistOrdering::from_str(s).map_err(de::Error::custom)
}
}

View file

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
use super::id::PlaylistId;
use serde::{Deserialize, Serialize};
use zbus::zvariant::{Type, Value};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Type, Value, Serialize, Deserialize)]
pub struct Playlist((PlaylistId, String, String));
impl Playlist {
pub fn id(&self) -> &PlaylistId {
&self.0 .0
}
pub fn name(&self) -> &str {
&self.0 .1
}
pub fn icon(&self) -> &str {
&self.0 .2
}
}

54
mpris2/src/track.rs Normal file
View file

@ -0,0 +1,54 @@
// SPDX-License-Identifier: MPL-2.0
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
fmt::{self, Display},
ops::Deref,
};
use zbus::zvariant::{ObjectPath, OwnedObjectPath, Type, Value};
/// A reference to an MPRIS track.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Type, Serialize, Deserialize, Value)]
pub struct TrackId(OwnedObjectPath);
impl TrackId {
pub fn into_inner(self) -> OwnedObjectPath {
self.0
}
pub fn into_static_path(self) -> ObjectPath<'static> {
self.0.into_inner().into_owned()
}
}
impl Deref for TrackId {
type Target = OwnedObjectPath;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> AsRef<ObjectPath<'a>> for TrackId {
fn as_ref(&self) -> &ObjectPath<'a> {
&self.0
}
}
impl PartialOrd for TrackId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.as_str().partial_cmp(other.0.as_str())
}
}
impl Ord for TrackId {
fn cmp(&self, other: &Self) -> Ordering {
self.0.as_str().cmp(other.0.as_str())
}
}
impl Display for TrackId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.as_str())
}
}

93
mpris2/src/track_list.rs Normal file
View file

@ -0,0 +1,93 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
bindings::track_list::TrackListProxy,
error::{Error, Result},
metadata::Metadata,
track::TrackId,
};
use std::{collections::BTreeMap, ops::Deref};
use zbus::{names::OwnedBusName, Connection};
#[derive(Debug, Clone)]
pub struct TrackList {
proxy: TrackListProxy<'static>,
}
impl TrackList {
/// Creates a new instance of the `org.mpris.MediaPlayer2.TrackList` interface.
pub async fn new(connection: &Connection, name: OwnedBusName) -> Result<Self> {
TrackListProxy::builder(connection)
.destination(name)?
.build()
.await
.map(Self::from)
.map_err(Error::from)
}
/// Adds a new track to this track list.
pub async fn add_track<S: ToString>(
&self,
uri: S,
after: &TrackId,
set_as_current: bool,
) -> Result<()> {
let uri = uri.to_string();
self.proxy
.add_track(&uri, after, set_as_current)
.await
.map_err(Error::from)
}
/// Gets the metadata of the given tracks.
pub async fn get_tracks_metadata<T: AsRef<[TrackId]>>(
&self,
tracks: T,
) -> Result<Vec<Metadata>> {
self.proxy
.get_tracks_metadata(tracks.as_ref().to_vec())
.await
.map(|x| x.into_iter().map(Metadata::from).collect())
.map_err(Error::from)
}
/// Goes to the specified track.
pub async fn go_to(&self, track: &TrackId) -> Result<()> {
self.proxy.go_to(track).await.map_err(Error::from)
}
/// Removes the specified track.
pub async fn remove(&self, track: &TrackId) -> Result<()> {
self.proxy.remove_track(track).await.map_err(Error::from)
}
/// Returns a list of all available [Track]s.
pub async fn tracks(&self) -> Result<Vec<TrackId>> {
self.proxy
.tracks()
.await
.map(|x| x.into_iter().map(TrackId::from).collect())
.map_err(Error::from)
}
/// Returns a list of all available [Track]s and their associated metadata,
/// in order.
pub async fn detailed_tracks(&self) -> Result<BTreeMap<TrackId, Metadata>> {
let tracks = self.tracks().await?;
let metadata = self.get_tracks_metadata(&tracks).await?;
Ok(tracks.into_iter().zip(metadata.into_iter()).collect())
}
}
impl Deref for TrackList {
type Target = TrackListProxy<'static>;
fn deref(&self) -> &Self::Target {
&self.proxy
}
}
impl From<TrackListProxy<'static>> for TrackList {
fn from(proxy: TrackListProxy<'static>) -> Self {
Self { proxy }
}
}

2
timedate/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
Cargo.lock

28
timedate/Cargo.toml Normal file
View file

@ -0,0 +1,28 @@
[package]
name = "timedate-zbus"
version = "0.1.0"
description = "a dbus client (using zbus) for timedate"
repository = "https://github.com/pop-os/dbus-settings-bindings"
license = "MPL-2.0"
edition = "2021"
categories = ["os::unix-apis"]
keywords = ["systemd", "timedate", "zbus"]
[package.metadata.docs.rs]
all-features = true
[dependencies]
zbus = "3.8.0"
[dev-dependencies]
chrono = "0.4.23"
chrono-tz = "0.8.1"
[dev-dependencies.zbus]
version = "3.8.0"
default-features = false
features = ["tokio"]
[dev-dependencies.tokio]
version = "1.25.0"
features = ["full"]

373
timedate/LICENSE Normal file
View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

3
timedate/README.md Normal file
View file

@ -0,0 +1,3 @@
# timedate-zbus
A zbus client proxy for [org.freedesktop.timedate1](https://www.freedesktop.org/software/systemd/man/org.freedesktop.timedate1.html). The [timedatectl example](./examples/timedatectl.rs) outputs in the same format as the `timedatectl` command.

View file

@ -0,0 +1,56 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use chrono::TimeZone;
const TZ_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S %Z";
const RTC_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S";
const CHOICES: &[&str] = &["no", "yes"];
#[tokio::main]
pub async fn main() -> zbus::Result<()> {
let connection = zbus::Connection::system().await?;
let proxy = timedate_zbus::TimeDateProxy::new(&connection).await?;
let ntp_service = if proxy.ntp().await? {
"active"
} else {
"inactive"
};
let rtc_in_local = proxy.local_rtc().await?;
let rtc_time_usecs = proxy.rtctime_usec().await?;
let time_usecs = proxy.time_usec().await?;
let timezone = proxy.timezone().await?;
let tz: chrono_tz::Tz = timezone.parse().unwrap();
let datetime = tz.timestamp_millis_opt((time_usecs / 1000) as i64).unwrap();
let rtc_millis = (rtc_time_usecs / 1000) as i64;
let rtc_time = (if rtc_in_local {
tz.timestamp_millis_opt(rtc_millis).unwrap()
} else {
chrono_tz::UTC.timestamp_millis_opt(rtc_millis).unwrap()
})
.format(RTC_FORMAT);
let local = datetime.format(TZ_FORMAT);
let universal = datetime.with_timezone(&chrono_tz::UTC).format(TZ_FORMAT);
let tz_string = datetime.format("%Z, %z");
let rtc_in_local = CHOICES[usize::from(rtc_in_local)];
let synchronized = CHOICES[usize::from(proxy.ntp_synchronized().await.unwrap_or_default())];
println!(
" Local time: {local}
Universal time: {universal}
RTC time: {rtc_time}
Time zone: {timezone} ({tz_string})
System clock synchronized: {synchronized}
NTP Service: {ntp_service}
RTC in local TZ: {rtc_in_local}"
);
Ok(())
}

75
timedate/src/lib.rs Normal file
View file

@ -0,0 +1,75 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
//! # DBus interface proxy for: `org.freedesktop.timedate1`
//!
//! This code was generated by `zbus-xmlgen` `3.1.0` from DBus introspection data.
//! Source: `Interface '/org/freedesktop/timedate1' from service 'org.freedesktop.timedate1' on system bus`.
//!
//! This DBus object implements
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
//!
//! * [`zbus::fdo::PeerProxy`]
//! * [`zbus::fdo::IntrospectableProxy`]
//! * [`zbus::fdo::PropertiesProxy`]
//!
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.freedesktop.timedate1",
default_service = "org.freedesktop.timedate1",
default_path = "/org/freedesktop/timedate1"
)]
trait TimeDate {
/// A list of time zones known on the local system as an array of names.
fn list_timezones(&self) -> zbus::Result<Vec<String>>;
/// Control whether the RTC is in local time zone or UTC.
#[dbus_proxy(name = "SetLocalRTC")]
fn set_local_rtc(
&self,
local_rtc: bool,
fix_system: bool,
interactive: bool,
) -> zbus::Result<()>;
/// Control whether the system clock is synchronized with the network using `systemd-timesyncd`.
#[dbus_proxy(name = "SetNTP")]
fn set_ntp(&self, use_ntp: bool, interactive: bool) -> zbus::Result<()>;
/// Change the system clock.
fn set_time(&self, usec_utc: i64, relative: bool, interactive: bool) -> zbus::Result<()>;
/// Set the system time zone.
fn set_timezone(&self, timezone: &str, interactive: bool) -> zbus::Result<()>;
/// Shows whether a service to perform time synchronization over network is available.
#[dbus_proxy(property, name = "CanNTP")]
fn can_ntp(&self) -> zbus::Result<bool>;
/// Shows whether the RTC is configured to use UTC or the local time zone.
#[dbus_proxy(property, name = "LocalRTC")]
fn local_rtc(&self) -> zbus::Result<bool>;
/// Shows whether the NTP service is enabled.
#[dbus_proxy(property, name = "NTP")]
fn ntp(&self) -> zbus::Result<bool>;
/// Shows whether the kernel reports the time as synchronized.
#[dbus_proxy(property, name = "NTPSynchronized")]
fn ntp_synchronized(&self) -> zbus::Result<bool>;
/// Shows the current time in RTC.
#[dbus_proxy(property, name = "RTCTimeUSec")]
fn rtctime_usec(&self) -> zbus::Result<u64>;
/// Shows the current time.
#[dbus_proxy(property, name = "TimeUSec")]
fn time_usec(&self) -> zbus::Result<u64>;
/// Shows the currently-configured time zone.
#[dbus_proxy(property)]
fn timezone(&self) -> zbus::Result<String>;
}

3
upower/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

19
upower/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "upower_dbus"
version = "0.3.2"
authors = ["Michael Murphy <mmstick@pm.me>"]
repository = "https://github.com/pop-os/dbus-settings-bindings"
description = "a dbus client (using zbus) for upower"
readme = "README.md"
license = "MIT"
categories = ["os::unix-apis"]
keywords = ["linux", "systemd", "logind", "dbus", "zbus"]
edition = "2018"
[dependencies]
serde = "1.0.152"
serde_repr = "0.1.10"
zbus = "3.8.0"
[dev-dependencies]
futures = "0.3.26"

373
upower/LICENSE Normal file
View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

56
upower/README.md Normal file
View file

@ -0,0 +1,56 @@
# upower-dbus
A Rust library which interfaces with UPower status information through dbus.
## Examples
### Detecting if the system is running on battery
```rust,no_run
extern crate upower_dbus;
use futures::stream::StreamExt;
use upower_dbus::UPowerProxy;
fn main() -> zbus::Result<()> {
futures::executor::block_on(async move {
let connection = zbus::Connection::system().await?;
let upower = UPowerProxy::new(&connection).await?;
println!("On Battery: {:?}", upower.on_battery().await);
let mut stream = upower.receive_on_battery_changed().await;
while let Some(event) = stream.next().await {
println!("On Battery: {:?}", event.get().await);
}
Ok(())
})
}
```
### Getting the current battery status as a percentage
```rust,no_run
extern crate upower_dbus;
use upower_dbus::UPowerProxy;
fn main() -> zbus::Result<()> {
futures::executor::block_on(async move {
let connection = zbus::Connection::system().await?;
let upower = UPowerProxy::new(&connection).await?;
let device = upower.get_display_device().await?;
println!("Battery: {:?}", device.percentage().await);
Ok(())
})
}
```

26
upower/examples/device.rs Normal file
View file

@ -0,0 +1,26 @@
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
extern crate upower_dbus;
use upower_dbus::UPowerProxy;
fn main() -> zbus::Result<()> {
futures::executor::block_on(async move {
let connection = zbus::Connection::system().await?;
let upower = UPowerProxy::new(&connection).await?;
let device = upower.get_display_device().await?;
println!("BatteryLevel: {:?}", device.battery_level().await);
println!("IconName: {:?}", device.icon_name().await);
println!("IsPresent: {:?}", device.is_present().await);
println!("Online: {:?}", device.online().await);
println!("Percentage: {:?}", device.percentage().await);
println!("State: {:?}", device.state().await);
println!("Type: {:?}", device.type_().await);
Ok(())
})
}

View file

@ -0,0 +1,25 @@
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
extern crate upower_dbus;
use futures::stream::StreamExt;
use upower_dbus::UPowerProxy;
fn main() -> zbus::Result<()> {
futures::executor::block_on(async move {
let connection = zbus::Connection::system().await?;
let upower = UPowerProxy::new(&connection).await?;
println!("On Battery: {:?}", upower.on_battery().await);
let mut stream = upower.receive_on_battery_changed().await;
while let Some(event) = stream.next().await {
eprintln!("{:?}", event.get().await);
}
Ok(())
})
}

122
upower/src/device.rs Normal file
View file

@ -0,0 +1,122 @@
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use serde_repr::{Deserialize_repr, Serialize_repr};
use zbus::dbus_proxy;
use zbus::zvariant::OwnedValue;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize_repr, Serialize_repr, OwnedValue)]
#[repr(u32)]
pub enum BatteryState {
Unknown = 0,
Charging = 1,
Discharging = 2,
Empty = 3,
FullyCharged = 4,
PendingCharge = 5,
PendingDischarge = 6,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize_repr, Serialize_repr, OwnedValue)]
#[repr(u32)]
pub enum BatteryType {
Unknown = 0,
LinePower = 1,
Battery = 2,
Ups = 3,
Monitor = 4,
Mouse = 5,
Keyboard = 6,
Pda = 7,
Phone = 8,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize_repr, Serialize_repr, OwnedValue)]
#[repr(u32)]
pub enum BatteryLevel {
Unknown = 0,
None = 1,
Low = 3,
Critical = 4,
Normal = 6,
High = 7,
Full = 8,
}
#[dbus_proxy(
interface = "org.freedesktop.UPower.Device",
default_service = "org.freedesktop.UPower",
assume_defaults = false
)]
trait Device {
#[dbus_proxy(property)]
fn battery_level(&self) -> zbus::Result<BatteryLevel>;
#[dbus_proxy(property)]
fn capacity(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn energy(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn energy_empty(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn energy_full(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn energy_full_design(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn has_history(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn has_statistics(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn icon_name(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn is_present(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn is_rechargeable(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn luminosity(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn model(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn native_path(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn online(&self) -> zbus::Result<bool>;
#[dbus_proxy(property)]
fn percentage(&self) -> zbus::Result<f64>;
#[dbus_proxy(property)]
fn power_supply(&self) -> zbus::Result<bool>;
fn refresh(&self) -> zbus::Result<()>;
#[dbus_proxy(property)]
fn serial(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn state(&self) -> zbus::Result<BatteryState>;
#[dbus_proxy(property)]
fn temperature(&self) -> zbus::Result<f64>;
#[dbus_proxy(property, name = "Type")]
fn type_(&self) -> zbus::Result<BatteryType>;
#[dbus_proxy(property)]
fn vendor(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
fn voltage(&self) -> zbus::Result<f64>;
}

9
upower/src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
#![doc = include_str!("../README.md")]
mod device;
mod upower;
pub use self::device::*;
pub use self::upower::*;

43
upower/src/upower.rs Normal file
View file

@ -0,0 +1,43 @@
// Copyright 2021 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use zbus::dbus_proxy;
use crate::device::{DeviceProxy, DeviceProxyBlocking};
#[dbus_proxy(interface = "org.freedesktop.UPower", assume_defaults = true)]
trait UPower {
/// EnumerateDevices method
fn enumerate_devices(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// GetCriticalAction method
fn get_critical_action(&self) -> zbus::Result<String>;
/// GetDisplayDevice method
#[dbus_proxy(object = "Device")]
fn get_display_device(&self);
/// DeviceAdded signal
#[dbus_proxy(signal)]
fn device_added(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// DeviceRemoved signal
#[dbus_proxy(signal)]
fn device_removed(&self, device: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// DaemonVersion property
#[dbus_proxy(property)]
fn daemon_version(&self) -> zbus::Result<String>;
/// LidIsClosed property
#[dbus_proxy(property)]
fn lid_is_closed(&self) -> zbus::Result<bool>;
/// LidIsPresent property
#[dbus_proxy(property)]
fn lid_is_present(&self) -> zbus::Result<bool>;
/// OnBattery property
#[dbus_proxy(property)]
fn on_battery(&self) -> zbus::Result<bool>;
}