fractal/src/system_settings.rs

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)
}
}