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                if let Ok(initial_size) = settings
 46                    .read::<u32>("org.gnome.desktop.interface", "cursor-size")
 47                    .await
 48                {
 49                    sender.send(Event::CursorSize(initial_size))?;
 50                }
 51
 52                if let Ok(mut cursor_theme_changed) = settings
 53                    .receive_setting_changed_with_args(
 54                        "org.gnome.desktop.interface",
 55                        "cursor-theme",
 56                    )
 57                    .await
 58                {
 59                    let sender = sender.clone();
 60                    background
 61                        .spawn(async move {
 62                            while let Some(theme) = cursor_theme_changed.next().await {
 63                                let theme = theme?;
 64                                sender.send(Event::CursorTheme(theme))?;
 65                            }
 66                            anyhow::Ok(())
 67                        })
 68                        .detach();
 69                }
 70
 71                if let Ok(mut cursor_size_changed) = settings
 72                    .receive_setting_changed_with_args::<u32>(
 73                        "org.gnome.desktop.interface",
 74                        "cursor-size",
 75                    )
 76                    .await
 77                {
 78                    let sender = sender.clone();
 79                    background
 80                        .spawn(async move {
 81                            while let Some(size) = cursor_size_changed.next().await {
 82                                let size = size?;
 83                                sender.send(Event::CursorSize(size))?;
 84                            }
 85                            anyhow::Ok(())
 86                        })
 87                        .detach();
 88                }
 89
 90                let mut appearance_changed = settings.receive_color_scheme_changed().await?;
 91                while let Some(scheme) = appearance_changed.next().await {
 92                    sender.send(Event::WindowAppearance(WindowAppearance::from_native(
 93                        scheme,
 94                    )))?;
 95                }
 96
 97                anyhow::Ok(())
 98            })
 99            .detach();
100
101        Self { channel }
102    }
103}
104
105impl EventSource for XDPEventSource {
106    type Event = Event;
107    type Metadata = ();
108    type Ret = ();
109    type Error = anyhow::Error;
110
111    fn process_events<F>(
112        &mut self,
113        readiness: Readiness,
114        token: Token,
115        mut callback: F,
116    ) -> Result<PostAction, Self::Error>
117    where
118        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
119    {
120        self.channel.process_events(readiness, token, |evt, _| {
121            if let calloop::channel::Event::Msg(msg) = evt {
122                (callback)(msg, &mut ())
123            }
124        })?;
125
126        Ok(PostAction::Continue)
127    }
128
129    fn register(
130        &mut self,
131        poll: &mut Poll,
132        token_factory: &mut TokenFactory,
133    ) -> calloop::Result<()> {
134        self.channel.register(poll, token_factory)?;
135
136        Ok(())
137    }
138
139    fn reregister(
140        &mut self,
141        poll: &mut Poll,
142        token_factory: &mut TokenFactory,
143    ) -> calloop::Result<()> {
144        self.channel.reregister(poll, token_factory)?;
145
146        Ok(())
147    }
148
149    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
150        self.channel.unregister(poll)?;
151
152        Ok(())
153    }
154}
155
156impl WindowAppearance {
157    fn from_native(cs: ColorScheme) -> WindowAppearance {
158        match cs {
159            ColorScheme::PreferDark => WindowAppearance::Dark,
160            ColorScheme::PreferLight => WindowAppearance::Light,
161            ColorScheme::NoPreference => WindowAppearance::Light,
162        }
163    }
164
165    #[cfg_attr(target_os = "linux", allow(dead_code))]
166    fn set_native(&mut self, cs: ColorScheme) {
167        *self = Self::from_native(cs);
168    }
169}