xdg_desktop_portal.rs

  1//! Provides a [calloop] event source from [XDG Desktop Portal] events
  2//!
  3//! This module uses the [ashpd] crate
  4
  5use ashpd::desktop::settings::{ColorScheme, Settings};
  6use calloop::channel::Channel;
  7use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
  8use smol::stream::StreamExt;
  9
 10use crate::{BackgroundExecutor, WindowAppearance};
 11
 12pub enum Event {
 13    WindowAppearance(WindowAppearance),
 14    #[cfg_attr(feature = "x11", allow(dead_code))]
 15    CursorTheme(String),
 16    #[cfg_attr(feature = "x11", allow(dead_code))]
 17    CursorSize(u32),
 18}
 19
 20pub struct XDPEventSource {
 21    channel: Channel<Event>,
 22}
 23
 24impl XDPEventSource {
 25    pub fn new(executor: &BackgroundExecutor) -> Self {
 26        let (sender, channel) = calloop::channel::channel();
 27
 28        let background = executor.clone();
 29
 30        executor
 31            .spawn(async move {
 32                let settings = Settings::new().await?;
 33
 34                if let Ok(initial_appearance) = settings.color_scheme().await {
 35                    sender.send(Event::WindowAppearance(WindowAppearance::from_native(
 36                        initial_appearance,
 37                    )))?;
 38                }
 39                if let Ok(initial_theme) = settings
 40                    .read::<String>("org.gnome.desktop.interface", "cursor-theme")
 41                    .await
 42                {
 43                    sender.send(Event::CursorTheme(initial_theme))?;
 44                }
 45
 46                // If u32 is used here, it throws invalid type error
 47                if let Ok(initial_size) = settings
 48                    .read::<i32>("org.gnome.desktop.interface", "cursor-size")
 49                    .await
 50                {
 51                    sender.send(Event::CursorSize(initial_size as u32))?;
 52                }
 53
 54                if let Ok(mut cursor_theme_changed) = settings
 55                    .receive_setting_changed_with_args(
 56                        "org.gnome.desktop.interface",
 57                        "cursor-theme",
 58                    )
 59                    .await
 60                {
 61                    let sender = sender.clone();
 62                    background
 63                        .spawn(async move {
 64                            while let Some(theme) = cursor_theme_changed.next().await {
 65                                let theme = theme?;
 66                                sender.send(Event::CursorTheme(theme))?;
 67                            }
 68                            anyhow::Ok(())
 69                        })
 70                        .detach();
 71                }
 72
 73                if let Ok(mut cursor_size_changed) = settings
 74                    .receive_setting_changed_with_args::<i32>(
 75                        "org.gnome.desktop.interface",
 76                        "cursor-size",
 77                    )
 78                    .await
 79                {
 80                    let sender = sender.clone();
 81                    background
 82                        .spawn(async move {
 83                            while let Some(size) = cursor_size_changed.next().await {
 84                                let size = size?;
 85                                sender.send(Event::CursorSize(size as u32))?;
 86                            }
 87                            anyhow::Ok(())
 88                        })
 89                        .detach();
 90                }
 91
 92                let mut appearance_changed = settings.receive_color_scheme_changed().await?;
 93                while let Some(scheme) = appearance_changed.next().await {
 94                    sender.send(Event::WindowAppearance(WindowAppearance::from_native(
 95                        scheme,
 96                    )))?;
 97                }
 98
 99                anyhow::Ok(())
100            })
101            .detach();
102
103        Self { channel }
104    }
105}
106
107impl EventSource for XDPEventSource {
108    type Event = Event;
109    type Metadata = ();
110    type Ret = ();
111    type Error = anyhow::Error;
112
113    fn process_events<F>(
114        &mut self,
115        readiness: Readiness,
116        token: Token,
117        mut callback: F,
118    ) -> Result<PostAction, Self::Error>
119    where
120        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
121    {
122        self.channel.process_events(readiness, token, |evt, _| {
123            if let calloop::channel::Event::Msg(msg) = evt {
124                (callback)(msg, &mut ())
125            }
126        })?;
127
128        Ok(PostAction::Continue)
129    }
130
131    fn register(
132        &mut self,
133        poll: &mut Poll,
134        token_factory: &mut TokenFactory,
135    ) -> calloop::Result<()> {
136        self.channel.register(poll, token_factory)?;
137
138        Ok(())
139    }
140
141    fn reregister(
142        &mut self,
143        poll: &mut Poll,
144        token_factory: &mut TokenFactory,
145    ) -> calloop::Result<()> {
146        self.channel.reregister(poll, token_factory)?;
147
148        Ok(())
149    }
150
151    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
152        self.channel.unregister(poll)?;
153
154        Ok(())
155    }
156}
157
158impl WindowAppearance {
159    fn from_native(cs: ColorScheme) -> WindowAppearance {
160        match cs {
161            ColorScheme::PreferDark => WindowAppearance::Dark,
162            ColorScheme::PreferLight => WindowAppearance::Light,
163            ColorScheme::NoPreference => WindowAppearance::Light,
164        }
165    }
166
167    #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
168    fn set_native(&mut self, cs: ColorScheme) {
169        *self = Self::from_native(cs);
170    }
171}