Compare commits

..

No commits in common. "507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" and "70d254bbae26085cc4728806fe6071c5a8a2a799" have entirely different histories.

17 changed files with 67 additions and 111 deletions

View file

@ -12,18 +12,17 @@ members = [
"locale1", "locale1",
"mpris2", "mpris2",
"networkmanager", "networkmanager",
"nm-secret-agent-manager",
"switcheroo-control", "switcheroo-control",
"timedate", "timedate",
"upower", "upower",
] ]
[workspace.dependencies] [workspace.dependencies]
futures-channel = "0.3.32" futures-channel = "0.3.31"
futures-util = "0.3.32" futures-util = "0.3.31"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
thiserror = "2.0" thiserror = "2.0"
jiff = "0.2" time = { version = "0.3", features = ["parsing"] }
tracing = "0.1.44" tracing = "0.1.41"
zbus = { version = "5.14.0" } zbus = { version = "5.11.0" }
zvariant = { version = "5.10.0" } zvariant = { version = "5.7.0" }

View file

@ -19,4 +19,4 @@ color-eyre = "0.6.5"
eyre = "0.6.12" eyre = "0.6.12"
pico-args = "0.5.0" pico-args = "0.5.0"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.20"

View file

@ -9,7 +9,7 @@ license = "MPL-2.0"
futures-util.workspace = true futures-util.workspace = true
serde.workspace = true serde.workspace = true
thiserror.workspace = true thiserror.workspace = true
jiff.workspace = true time.workspace = true
zbus.workspace = true zbus.workspace = true
zvariant.workspace = true zvariant.workspace = true

View file

@ -43,7 +43,7 @@ async fn main() -> Result<()> {
.await .await
.into_diagnostic() .into_diagnostic()
.wrap_err_with(|| format!("Failed to get position for media player '{}'", name))? .wrap_err_with(|| format!("Failed to get position for media player '{}'", name))?
.map(|s| format!("{} seconds", s.as_secs_f32())) .map(|s| format!("{} seconds", s.as_seconds_f32()))
.unwrap_or_else(|| "N/A".to_owned()); .unwrap_or_else(|| "N/A".to_owned());
println!("\tPosition: {}", position); println!("\tPosition: {}", position);
if !player if !player

View file

@ -1,11 +1,11 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use jiff::{SignedDuration, Timestamp};
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt, fmt,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use time::{Duration, OffsetDateTime};
use zbus::zvariant::{OwnedObjectPath, Value as ZValue}; use zbus::zvariant::{OwnedObjectPath, Value as ZValue};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -90,7 +90,7 @@ impl Metadata {
} }
/// `xesam:contentCreated`: When the track was created. Usually only the year component will be useful. /// `xesam:contentCreated`: When the track was created. Usually only the year component will be useful.
pub fn created(&self) -> Option<Timestamp> { pub fn created(&self) -> Option<OffsetDateTime> {
self.inner self.inner
.get("xesam:contentCreated") .get("xesam:contentCreated")
.cloned() .cloned()
@ -106,7 +106,7 @@ impl Metadata {
} }
/// `xesam:firstUsed`: When the track was first played. /// `xesam:firstUsed`: When the track was first played.
pub fn first_played(&self) -> Option<Timestamp> { pub fn first_played(&self) -> Option<OffsetDateTime> {
self.inner self.inner
.get("xesam:firstUsed") .get("xesam:firstUsed")
.cloned() .cloned()
@ -128,7 +128,7 @@ impl Metadata {
} }
/// `xesam:lastUsed`: When the track was last played. /// `xesam:lastUsed`: When the track was last played.
pub fn last_played(&self) -> Option<Timestamp> { pub fn last_played(&self) -> Option<OffsetDateTime> {
self.inner self.inner
.get("xesam:lastUsed") .get("xesam:lastUsed")
.cloned() .cloned()
@ -199,7 +199,7 @@ impl Metadata {
} }
/// `mpris:length`: The length of the track in microseconds. /// `mpris:length`: The length of the track in microseconds.
pub fn length(&self) -> Option<SignedDuration> { pub fn length(&self) -> Option<Duration> {
self.inner self.inner
.get("mpris:length") .get("mpris:length")
.cloned() .cloned()
@ -209,7 +209,7 @@ impl Metadata {
MetadataValue::Str(s) => s.parse().ok(), MetadataValue::Str(s) => s.parse().ok(),
_ => None, _ => None,
}) })
.map(SignedDuration::from_micros) .map(Duration::microseconds)
} }
/// `mpris:artUrl`: The location of an image representing the track or album. /// `mpris:artUrl`: The location of an image representing the track or album.
@ -309,13 +309,17 @@ impl MetadataValue {
/// Tries to extract a date/time from the variant, /// Tries to extract a date/time from the variant,
/// returning an error if the variant is not a date/time. /// returning an error if the variant is not a date/time.
pub fn try_into_date(self) -> Result<Timestamp> { pub fn try_into_date(self) -> Result<OffsetDateTime> {
let variant = self.variant(); let variant = self.variant();
match self { match self {
MetadataValue::Str(s) => s.parse::<Timestamp>().map_err(|_| Error::IncorrectVariant { MetadataValue::Str(s) => {
wanted: "String (DateTime)", OffsetDateTime::parse(&s, &time::format_description::well_known::Rfc3339).map_err(
actual: variant, |_| Error::IncorrectVariant {
}), wanted: "String (DateTime)",
actual: variant,
},
)
}
_ => Err(Error::IncorrectVariant { _ => Err(Error::IncorrectVariant {
wanted: "String (DateTime)", wanted: "String (DateTime)",
actual: variant, actual: variant,
@ -325,7 +329,7 @@ impl MetadataValue {
/// Tries to extract a date/time from the variant, /// Tries to extract a date/time from the variant,
/// panicking if the variant is not a date/time. /// panicking if the variant is not a date/time.
pub fn into_date(self) -> Timestamp { pub fn into_date(self) -> OffsetDateTime {
self.try_into_date().unwrap_or_else(|err| panic!("{}", err)) self.try_into_date().unwrap_or_else(|err| panic!("{}", err))
} }

View file

@ -7,12 +7,12 @@ use crate::{
metadata::Metadata, metadata::Metadata,
track::TrackId, track::TrackId,
}; };
use jiff::SignedDuration;
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display},
ops::Deref, ops::Deref,
str::FromStr, str::FromStr,
}; };
use time::Duration;
use zbus::{Connection, names::OwnedBusName}; use zbus::{Connection, names::OwnedBusName};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -41,9 +41,11 @@ impl Player {
} }
/// Seeks the specified duration. /// Seeks the specified duration.
pub async fn seek(&self, duration: SignedDuration) -> Result<bool> { pub async fn seek(&self, duration: Duration) -> Result<bool> {
if self.proxy.can_seek().await? { if self.proxy.can_seek().await? {
self.proxy.seek(duration.as_micros() as i64).await?; self.proxy
.seek(duration.whole_microseconds() as i64)
.await?;
Ok(true) Ok(true)
} else { } else {
Ok(false) Ok(false)
@ -53,9 +55,9 @@ impl Player {
/// Sets the current track position. /// Sets the current track position.
/// ///
/// If `track` does not match the id of the currently-playing track, the call is ignored as "stale". /// 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: SignedDuration) -> Result<()> { pub async fn set_position(&self, track: &TrackId, position: Duration) -> Result<()> {
self.proxy self.proxy
.set_position(track, position.as_micros() as i64) .set_position(track, position.whole_microseconds() as i64)
.await .await
.map_err(Error::from) .map_err(Error::from)
} }
@ -63,8 +65,8 @@ impl Player {
/// How far into the current track the player is. /// How far into the current track the player is.
/// ///
/// Not all players support this, and it will return None if this is the case. /// Not all players support this, and it will return None if this is the case.
pub async fn position(&self) -> Result<Option<SignedDuration>> { pub async fn position(&self) -> Result<Option<Duration>> {
handle_optional(self.proxy.position().await.map(SignedDuration::from_micros)) handle_optional(self.proxy.position().await.map(Duration::microseconds))
} }
/// Gets the current playback status of the player. /// Gets the current playback status of the player.

View file

@ -5,9 +5,9 @@ edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
[dependencies] [dependencies]
bitflags = "2.11" bitflags = "2.9"
derive_builder = "0.20.2" derive_builder = "0.20.2"
procfs = { version = "0.18", default-features = false } procfs = { version = "0.18", default-features = false }
jiff.workspace = true time.workspace = true
zvariant.workspace = true zvariant.workspace = true
zbus.workspace = true zbus.workspace = true

View file

@ -8,13 +8,14 @@ use crate::{
util::clock_boottime_to_time, util::clock_boottime_to_time,
}; };
use std::ops::Deref; use std::ops::Deref;
use time::OffsetDateTime;
use zbus::Result; use zbus::Result;
#[derive(Debug)] #[derive(Debug)]
pub struct AccessPoint<'a>(AccessPointProxy<'a>); pub struct AccessPoint<'a>(AccessPointProxy<'a>);
impl<'a> AccessPoint<'a> { impl<'a> AccessPoint<'a> {
pub async fn last_seen(&self) -> Result<Option<jiff::Timestamp>> { pub async fn last_seen(&self) -> Result<Option<OffsetDateTime>> {
Ok(clock_boottime_to_time(self.0.last_seen().await?)) Ok(clock_boottime_to_time(self.0.last_seen().await?))
} }

View file

@ -1,10 +1,10 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use jiff::Timestamp; use time::OffsetDateTime;
pub fn clock_boottime_to_time(time: i32) -> Option<Timestamp> { pub fn clock_boottime_to_time(time: i32) -> Option<OffsetDateTime> {
let boot_time = procfs::boot_time_secs() let boot_time = procfs::boot_time_secs()
.ok() .ok()
.and_then(|boot_time| i64::try_from(boot_time).ok())?; .and_then(|boot_time| i64::try_from(boot_time).ok())?;
Timestamp::from_second(boot_time + time as i64).ok() OffsetDateTime::from_unix_timestamp(boot_time + time as i64).ok()
} }

View file

@ -1,7 +0,0 @@
[package]
name = "nm-secret-agent-manager"
version = "0.1.0"
edition = "2024"
[dependencies]
zbus.workspace = true

View file

@ -1,30 +0,0 @@
//! # D-Bus interface proxy for: `org.freedesktop.NetworkManager.AgentManager`
//!
//! This code was generated by `zbus-xmlgen` `5.0.0` from D-Bus introspection data.
//! Source: `test.xml`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
//!
//! More information can be found in the [Writing a client proxy] section of the zbus
//! documentation.
//!
//!
//! [Writing a client proxy]: https://dbus2.github.io/zbus/client.html
//! [D-Bus standard interfaces]: https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces,
use zbus::proxy;
#[proxy(
interface = "org.freedesktop.NetworkManager.AgentManager",
default_service = "org.freedesktop.NetworkManager",
default_path = "/org/freedesktop/NetworkManager/AgentManager",
assume_defaults = true
)]
pub trait AgentManager {
/// Register method
fn register(&self, identifier: &str) -> zbus::Result<()>;
/// RegisterWithCapabilities method
fn register_with_capabilities(&self, identifier: &str, capabilities: u32) -> zbus::Result<()>;
/// Unregister method
fn unregister(&self) -> zbus::Result<()>;
}

View file

@ -99,20 +99,15 @@ impl TryFrom<HashMap<String, OwnedValue>> for Gpu {
.chunks_exact(2) .chunks_exact(2)
.map(|chunk| (chunk[0].clone(), chunk[1].clone())) .map(|chunk| (chunk[0].clone(), chunk[1].clone()))
.collect(); .collect();
let default: bool = value let default = value
.get("Default") .get("Default")
.ok_or(zvariant::Error::IncorrectType)? .ok_or(zvariant::Error::IncorrectType)?
.try_into()?; .try_into()?;
let discrete = value
.get("Discrete")
.and_then(|v| v.try_into().ok())
.unwrap_or(!default);
Ok(Self { Ok(Self {
name, name,
environment, environment,
default, default,
discrete,
}) })
} }
} }
@ -130,5 +125,4 @@ pub struct Gpu {
pub name: String, pub name: String,
pub environment: HashMap<String, String>, pub environment: HashMap<String, String>,
pub default: bool, pub default: bool,
pub discrete: bool,
} }

View file

@ -15,10 +15,11 @@ all-features = true
zbus.workspace = true zbus.workspace = true
[dev-dependencies] [dev-dependencies]
jiff.workspace = true chrono = "0.4.42"
chrono-tz = "0.10.4"
zbus.workspace = true zbus.workspace = true
zbus.features = ["tokio"] zbus.features = ["tokio"]
[dev-dependencies.tokio] [dev-dependencies.tokio]
version = "1.50.0" version = "1.47.1"
features = ["full"] features = ["full"]

View file

@ -1,7 +1,7 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use jiff::{Timestamp, tz::TimeZone}; use chrono::TimeZone;
const TZ_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S %Z"; const TZ_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S %Z";
const RTC_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S"; const RTC_FORMAT: &str = "%a %Y-%m-%d %H:%M:%S";
@ -23,24 +23,21 @@ pub async fn main() -> zbus::Result<()> {
let time_usecs = proxy.time_usec().await?; let time_usecs = proxy.time_usec().await?;
let timezone = proxy.timezone().await?; let timezone = proxy.timezone().await?;
let tz = TimeZone::get(&timezone).unwrap(); let tz: chrono_tz::Tz = timezone.parse().unwrap();
let utc = TimeZone::UTC;
let datetime = Timestamp::from_microsecond(time_usecs as i64) let datetime = tz.timestamp_millis_opt((time_usecs / 1000) as i64).unwrap();
.unwrap()
.to_zoned(tz.clone());
let rtc_ts = Timestamp::from_microsecond(rtc_time_usecs as i64).unwrap(); let rtc_millis = (rtc_time_usecs / 1000) as i64;
let rtc_time = (if rtc_in_local { let rtc_time = (if rtc_in_local {
rtc_ts.to_zoned(tz) tz.timestamp_millis_opt(rtc_millis).unwrap()
} else { } else {
rtc_ts.to_zoned(utc.clone()) chrono_tz::UTC.timestamp_millis_opt(rtc_millis).unwrap()
}) })
.strftime(RTC_FORMAT); .format(RTC_FORMAT);
let local = datetime.strftime(TZ_FORMAT); let local = datetime.format(TZ_FORMAT);
let universal = datetime.with_time_zone(utc).strftime(TZ_FORMAT); let universal = datetime.with_timezone(&chrono_tz::UTC).format(TZ_FORMAT);
let tz_string = datetime.strftime("%Z, %z"); let tz_string = datetime.format("%Z, %z");
let rtc_in_local = CHOICES[usize::from(rtc_in_local)]; let rtc_in_local = CHOICES[usize::from(rtc_in_local)];
let synchronized = CHOICES[usize::from(proxy.ntp_synchronized().await.unwrap_or_default())]; let synchronized = CHOICES[usize::from(proxy.ntp_synchronized().await.unwrap_or_default())];

View file

@ -46,7 +46,7 @@ pub trait TimeDate {
fn set_timezone(&self, timezone: &str, interactive: bool) -> zbus::Result<()>; fn set_timezone(&self, timezone: &str, interactive: bool) -> zbus::Result<()>;
/// Shows whether a service to perform time synchronization over network is available. /// Shows whether a service to perform time synchronization over network is available.
#[zbus(property(emits_changed_signal = "false"), name = "CanNTP")] #[zbus(property, name = "CanNTP")]
fn can_ntp(&self) -> zbus::Result<bool>; fn can_ntp(&self) -> zbus::Result<bool>;
/// Shows whether the RTC is configured to use UTC or the local time zone. /// Shows whether the RTC is configured to use UTC or the local time zone.
@ -58,15 +58,15 @@ pub trait TimeDate {
fn ntp(&self) -> zbus::Result<bool>; fn ntp(&self) -> zbus::Result<bool>;
/// Shows whether the kernel reports the time as synchronized. /// Shows whether the kernel reports the time as synchronized.
#[zbus(property(emits_changed_signal = "false"), name = "NTPSynchronized")] #[zbus(property, name = "NTPSynchronized")]
fn ntp_synchronized(&self) -> zbus::Result<bool>; fn ntp_synchronized(&self) -> zbus::Result<bool>;
/// Shows the current time in RTC. /// Shows the current time in RTC.
#[zbus(property(emits_changed_signal = "false"), name = "RTCTimeUSec")] #[zbus(property, name = "RTCTimeUSec")]
fn rtctime_usec(&self) -> zbus::Result<u64>; fn rtctime_usec(&self) -> zbus::Result<u64>;
/// Shows the current time. /// Shows the current time.
#[zbus(property(emits_changed_signal = "false"), name = "TimeUSec")] #[zbus(property, name = "TimeUSec")]
fn time_usec(&self) -> zbus::Result<u64>; fn time_usec(&self) -> zbus::Result<u64>;
/// Shows the currently-configured time zone. /// Shows the currently-configured time zone.

View file

@ -16,4 +16,4 @@ serde_repr = "0.1.20"
zbus.workspace = true zbus.workspace = true
[dev-dependencies] [dev-dependencies]
futures = "0.3.32" futures = "0.3.31"

View file

@ -132,7 +132,7 @@ pub trait Device {
#[zbus(property)] #[zbus(property)]
fn is_rechargeable(&self) -> zbus::Result<bool>; fn is_rechargeable(&self) -> zbus::Result<bool>;
#[deprecated(since = "0.3.2", note = "deprecated since 0.99.12")] #[deprecated(since="0.3.2", note="deprecated since 0.99.12")]
#[zbus(property)] #[zbus(property)]
fn luminosity(&self) -> zbus::Result<f64>; fn luminosity(&self) -> zbus::Result<f64>;
@ -183,21 +183,16 @@ pub trait Device {
#[zbus(property)] #[zbus(property)]
fn voltage_min_design(&self) -> zbus::Result<f64>; fn voltage_min_design(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn voltage_max_design(&self) -> zbus::Result<f64>;
fn get_history(
&self,
type_: String,
timespan: u32,
resolution: u32,
) -> zbus::Result<Vec<(u32, f64, u32)>>;
fn get_statistics(&self, type_: String) -> zbus::Result<Vec<(f64, f64)>>;
#[zbus(signal)] #[zbus(signal)]
fn enable_charge_threshold(&self, message: bool) -> zbus::Result<()>; fn enable_charge_threshold(&self, message: bool) -> zbus::Result<()>;
#[zbus(signal)]
fn get_history(&self, type_: String, timespan: u32, resolution: u32) -> zbus::Result<Vec<u32, f64, u32>>;
#[zbus(signal)]
fn get_statistics(&self, type_: String) -> zbus::Result<()>;
#[zbus(signal)] #[zbus(signal)]
fn refresh(&self) -> zbus::Result<()>; fn refresh(&self) -> zbus::Result<()>;
} }