fractal/system_settings/
linux.rs

1use 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        /// Initialize the system settings.
45        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    /// API to access system settings on Linux.
123    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}