fractal/system_settings/
linux.rs1use std::sync::Arc;
2
3use ashpd::{desktop::settings::Settings as SettingsProxy, zvariant};
4use futures_util::StreamExt;
5use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
6use tracing::error;
7
8use super::{ClockFormat, SystemSettings, SystemSettingsImpl};
9use crate::{spawn, spawn_tokio};
10
11const GNOME_DESKTOP_NAMESPACE: &str = "org.gnome.desktop.interface";
12const CLOCK_FORMAT_KEY: &str = "clock-format";
13
14mod imp {
15 use super::*;
16
17 #[derive(Debug, Default)]
18 pub struct LinuxSystemSettings {}
19
20 #[glib::object_subclass]
21 impl ObjectSubclass for LinuxSystemSettings {
22 const NAME: &'static str = "LinuxSystemSettings";
23 type Type = super::LinuxSystemSettings;
24 type ParentType = SystemSettings;
25 }
26
27 impl ObjectImpl for LinuxSystemSettings {
28 fn constructed(&self) {
29 self.parent_constructed();
30
31 spawn!(clone!(
32 #[weak(rename_to = imp)]
33 self,
34 async move {
35 imp.init().await;
36 }
37 ));
38 }
39 }
40
41 impl SystemSettingsImpl for LinuxSystemSettings {}
42
43 impl LinuxSystemSettings {
44 async fn init(&self) {
46 let obj = self.obj();
47
48 let proxy = match spawn_tokio!(async move { SettingsProxy::new().await })
49 .await
50 .expect("task was not aborted")
51 {
52 Ok(proxy) => proxy,
53 Err(error) => {
54 error!("Could not access settings portal: {error}");
55 return;
56 }
57 };
58 let proxy = Arc::new(proxy);
59
60 let proxy_clone = proxy.clone();
61 match spawn_tokio!(async move {
62 proxy_clone
63 .read::<ClockFormat>(GNOME_DESKTOP_NAMESPACE, CLOCK_FORMAT_KEY)
64 .await
65 })
66 .await
67 .expect("task was not aborted")
68 {
69 Ok(clock_format) => obj
70 .upcast_ref::<SystemSettings>()
71 .set_clock_format(clock_format),
72 Err(error) => {
73 error!("Could not access clock format system setting: {error}");
74 return;
75 }
76 }
77
78 let clock_format_changed_stream = match spawn_tokio!(async move {
79 proxy
80 .receive_setting_changed_with_args::<ClockFormat>(
81 GNOME_DESKTOP_NAMESPACE,
82 CLOCK_FORMAT_KEY,
83 )
84 .await
85 })
86 .await
87 .expect("task was not aborted")
88 {
89 Ok(stream) => stream,
90 Err(error) => {
91 error!(
92 "Could not listen to changes of the clock format system setting: {error}"
93 );
94 return;
95 }
96 };
97
98 let obj_weak = obj.downgrade();
99 clock_format_changed_stream.for_each(move |value| {
100 let obj_weak = obj_weak.clone();
101 async move {
102 let clock_format = match value {
103 Ok(clock_format) => clock_format,
104 Err(error) => {
105 error!("Could not update clock format setting: {error}");
106 return;
107 }
108 };
109
110 if let Some(obj) = obj_weak.upgrade() {
111 obj.upcast_ref::<SystemSettings>().set_clock_format(clock_format);
112 } else {
113 error!("Could not update clock format setting: could not upgrade weak reference");
114 }
115 }
116 }).await;
117 }
118 }
119}
120
121glib::wrapper! {
122 pub struct LinuxSystemSettings(ObjectSubclass<imp::LinuxSystemSettings>)
124 @extends SystemSettings;
125}
126
127impl LinuxSystemSettings {
128 pub fn new() -> Self {
129 glib::Object::new()
130 }
131}
132
133impl Default for LinuxSystemSettings {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl TryFrom<&zvariant::OwnedValue> for ClockFormat {
140 type Error = zvariant::Error;
141
142 fn try_from(value: &zvariant::OwnedValue) -> Result<Self, Self::Error> {
143 let Ok(s) = <&str>::try_from(value) else {
144 return Err(zvariant::Error::IncorrectType);
145 };
146
147 match s {
148 "12h" => Ok(Self::TwelveHours),
149 "24h" => Ok(Self::TwentyFourHours),
150 _ => Err(zvariant::Error::Message(format!(
151 "Invalid string `{s}`, expected `12h` or `24h`"
152 ))),
153 }
154 }
155}
156
157impl TryFrom<zvariant::OwnedValue> for ClockFormat {
158 type Error = zvariant::Error;
159
160 fn try_from(value: zvariant::OwnedValue) -> Result<Self, Self::Error> {
161 Self::try_from(&value)
162 }
163}