commit
dd3d4935a9
37 changed files with 3421 additions and 1 deletions
|
|
@ -1,2 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["networkmanager"]
|
members = [
|
||||||
|
"networkmanager",
|
||||||
|
"timedate",
|
||||||
|
"upower",
|
||||||
|
]
|
||||||
|
|
|
||||||
2
mpris2/.gitignore
vendored
Normal file
2
mpris2/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
17
mpris2/Cargo.toml
Normal file
17
mpris2/Cargo.toml
Normal 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
346
mpris2/LICENSE.md
Normal 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
3
mpris2/README.md
Normal 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
156
mpris2/examples/list.rs
Normal 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
5
mpris2/src/bindings.rs
Normal 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;
|
||||||
63
mpris2/src/bindings/media_player.rs
Normal file
63
mpris2/src/bindings/media_player.rs
Normal 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>>;
|
||||||
|
}
|
||||||
131
mpris2/src/bindings/player.rs
Normal file
131
mpris2/src/bindings/player.rs
Normal 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<()>;
|
||||||
|
}
|
||||||
57
mpris2/src/bindings/playlist.rs
Normal file
57
mpris2/src/bindings/playlist.rs
Normal 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>;
|
||||||
|
}
|
||||||
58
mpris2/src/bindings/track_list.rs
Normal file
58
mpris2/src/bindings/track_list.rs
Normal 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
51
mpris2/src/error.rs
Normal 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
21
mpris2/src/lib.rs
Normal 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
111
mpris2/src/media_player.rs
Normal 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
533
mpris2/src/metadata.rs
Normal 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
267
mpris2/src/player.rs
Normal 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
41
mpris2/src/playlists.rs
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
53
mpris2/src/playlists/id.rs
Normal file
53
mpris2/src/playlists/id.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
120
mpris2/src/playlists/ordering.rs
Normal file
120
mpris2/src/playlists/ordering.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
mpris2/src/playlists/playlist.rs
Normal file
21
mpris2/src/playlists/playlist.rs
Normal 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
54
mpris2/src/track.rs
Normal 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
93
mpris2/src/track_list.rs
Normal 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
2
timedate/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
28
timedate/Cargo.toml
Normal file
28
timedate/Cargo.toml
Normal 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
373
timedate/LICENSE
Normal 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
3
timedate/README.md
Normal 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.
|
||||||
56
timedate/examples/timedatectl.rs
Normal file
56
timedate/examples/timedatectl.rs
Normal 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
75
timedate/src/lib.rs
Normal 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
3
upower/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
19
upower/Cargo.toml
Normal file
19
upower/Cargo.toml
Normal 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
373
upower/LICENSE
Normal 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
56
upower/README.md
Normal 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
26
upower/examples/device.rs
Normal 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(())
|
||||||
|
})
|
||||||
|
}
|
||||||
25
upower/examples/on_battery.rs
Normal file
25
upower/examples/on_battery.rs
Normal 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
122
upower/src/device.rs
Normal 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
9
upower/src/lib.rs
Normal 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
43
upower/src/upower.rs
Normal 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>;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue