199 lines
5.6 KiB
Rust
199 lines
5.6 KiB
Rust
use std::sync::Arc;
|
|
|
|
use ashpd::{desktop::settings::Settings as SettingsProxy, zvariant};
|
|
use futures_util::StreamExt;
|
|
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
|
|
use tracing::error;
|
|
|
|
use crate::{spawn, spawn_tokio};
|
|
|
|
const GNOME_DESKTOP_NAMESPACE: &str = "org.gnome.desktop.interface";
|
|
|
|
mod imp {
|
|
use std::cell::Cell;
|
|
|
|
use super::*;
|
|
|
|
#[derive(Debug, glib::Properties)]
|
|
#[properties(wrapper_type = super::SystemSettings)]
|
|
pub struct SystemSettings {
|
|
/// The clock format setting.
|
|
#[property(get, builder(ClockFormat::default()))]
|
|
pub clock_format: Cell<ClockFormat>,
|
|
}
|
|
|
|
impl Default for SystemSettings {
|
|
fn default() -> Self {
|
|
// Use the locale's default clock format as a fallback.
|
|
let local_formatted_time = glib::DateTime::now_local()
|
|
.and_then(|d| d.format("%X"))
|
|
.map(|s| s.to_ascii_lowercase());
|
|
let clock_format = match &local_formatted_time {
|
|
Ok(s) if s.ends_with("am") || s.ends_with("pm") => ClockFormat::TwelveHours,
|
|
Ok(_) => ClockFormat::TwentyFourHours,
|
|
Err(error) => {
|
|
error!("Failed to get local formatted time: {error}");
|
|
ClockFormat::TwelveHours
|
|
}
|
|
};
|
|
|
|
Self {
|
|
clock_format: Cell::new(clock_format),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[glib::object_subclass]
|
|
impl ObjectSubclass for SystemSettings {
|
|
const NAME: &'static str = "SystemSettings";
|
|
type Type = super::SystemSettings;
|
|
}
|
|
|
|
#[glib::derived_properties]
|
|
impl ObjectImpl for SystemSettings {
|
|
fn constructed(&self) {
|
|
self.parent_constructed();
|
|
let obj = self.obj();
|
|
|
|
spawn!(clone!(@weak obj => async move {
|
|
obj.init().await;
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
glib::wrapper! {
|
|
/// An API to access system settings.
|
|
pub struct SystemSettings(ObjectSubclass<imp::SystemSettings>);
|
|
}
|
|
|
|
impl SystemSettings {
|
|
pub fn new() -> Self {
|
|
glib::Object::new()
|
|
}
|
|
|
|
/// Initialize the system settings.
|
|
async fn init(&self) {
|
|
let proxy = match spawn_tokio!(async move { SettingsProxy::new().await })
|
|
.await
|
|
.unwrap()
|
|
{
|
|
Ok(proxy) => proxy,
|
|
Err(error) => {
|
|
error!("Failed to access settings portal: {error}");
|
|
return;
|
|
}
|
|
};
|
|
let proxy = Arc::new(proxy);
|
|
|
|
let proxy_clone = proxy.clone();
|
|
match spawn_tokio!(async move {
|
|
proxy_clone
|
|
.read::<ClockFormat>(GNOME_DESKTOP_NAMESPACE, ClockFormat::KEY)
|
|
.await
|
|
})
|
|
.await
|
|
.unwrap()
|
|
{
|
|
Ok(clock_format) => self.set_clock_format(clock_format),
|
|
Err(error) => {
|
|
error!("Failed to access clock format system setting: {error}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let clock_format_changed_stream = match spawn_tokio!(async move {
|
|
proxy
|
|
.receive_setting_changed_with_args(GNOME_DESKTOP_NAMESPACE, ClockFormat::KEY)
|
|
.await
|
|
})
|
|
.await
|
|
.unwrap()
|
|
{
|
|
Ok(stream) => stream,
|
|
Err(error) => {
|
|
error!("Failed to listen to changes of the clock format system setting: {error}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let obj_weak = self.downgrade();
|
|
clock_format_changed_stream.for_each(move |setting| {
|
|
let obj_weak = obj_weak.clone();
|
|
async move {
|
|
let clock_format = match ClockFormat::try_from(setting.value()) {
|
|
Ok(clock_format) => clock_format,
|
|
Err(error) => {
|
|
error!("Could not update clock format setting: {error}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
if let Some(obj) = obj_weak.upgrade() {
|
|
obj.set_clock_format(clock_format);
|
|
} else {
|
|
error!("Could not update clock format setting: could not upgrade weak reference");
|
|
}
|
|
}
|
|
}).await;
|
|
}
|
|
|
|
/// Set the clock format setting.
|
|
fn set_clock_format(&self, clock_format: ClockFormat) {
|
|
if self.clock_format() == clock_format {
|
|
return;
|
|
}
|
|
|
|
self.imp().clock_format.set(clock_format);
|
|
self.notify_clock_format();
|
|
}
|
|
}
|
|
|
|
impl Default for SystemSettings {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// The clock format setting.
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum)]
|
|
#[repr(u32)]
|
|
#[enum_type(name = "ClockFormat")]
|
|
pub enum ClockFormat {
|
|
/// The 12h format, i.e. AM/PM.
|
|
#[default]
|
|
TwelveHours = 0,
|
|
/// The 24h format.
|
|
TwentyFourHours = 1,
|
|
}
|
|
|
|
impl ClockFormat {
|
|
const KEY: &'static str = "clock-format";
|
|
}
|
|
|
|
impl TryFrom<&zvariant::OwnedValue> for ClockFormat {
|
|
type Error = zvariant::Error;
|
|
|
|
fn try_from(value: &zvariant::OwnedValue) -> Result<Self, Self::Error> {
|
|
let Ok(s) = <&str>::try_from(value) else {
|
|
return Err(zvariant::Error::IncorrectType);
|
|
};
|
|
|
|
match s {
|
|
"12h" => Ok(Self::TwelveHours),
|
|
"24h" => Ok(Self::TwentyFourHours),
|
|
_ => Err(zvariant::Error::Message(format!(
|
|
"Invalid string `{s}`, expected `12h` or `24h`"
|
|
))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<zvariant::OwnedValue> for ClockFormat {
|
|
type Error = zvariant::Error;
|
|
|
|
fn try_from(value: zvariant::OwnedValue) -> Result<Self, Self::Error> {
|
|
Self::try_from(&value)
|
|
}
|
|
}
|