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 gpui::{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    ButtonLayout(String),
 19}
 20
 21pub struct XDPEventSource {
 22    channel: Channel<Event>,
 23}
 24
 25impl XDPEventSource {
 26    pub fn new(executor: &BackgroundExecutor) -> Self {
 27        let (sender, channel) = calloop::channel::channel();
 28
 29        let background = executor.clone();
 30
 31        executor
 32            .spawn(async move {
 33                let settings = Settings::new().await?;
 34
 35                if let Ok(initial_appearance) = settings.color_scheme().await {
 36                    sender.send(Event::WindowAppearance(
 37                        window_appearance_from_color_scheme(initial_appearance),
 38                    ))?;
 39                }
 40                if let Ok(initial_theme) = settings
 41                    .read::<String>("org.gnome.desktop.interface", "cursor-theme")
 42                    .await
 43                {
 44                    sender.send(Event::CursorTheme(initial_theme))?;
 45                }
 46
 47                // If u32 is used here, it throws invalid type error
 48                if let Ok(initial_size) = settings
 49                    .read::<i32>("org.gnome.desktop.interface", "cursor-size")
 50                    .await
 51                {
 52                    sender.send(Event::CursorSize(initial_size as u32))?;
 53                }
 54
 55                if let Ok(initial_layout) = settings
 56                    .read::<String>("org.gnome.desktop.wm.preferences", "button-layout")
 57                    .await
 58                {
 59                    sender.send(Event::ButtonLayout(initial_layout))?;
 60                }
 61
 62                if let Ok(mut cursor_theme_changed) = settings
 63                    .receive_setting_changed_with_args(
 64                        "org.gnome.desktop.interface",
 65                        "cursor-theme",
 66                    )
 67                    .await
 68                {
 69                    let sender = sender.clone();
 70                    background
 71                        .spawn(async move {
 72                            while let Some(theme) = cursor_theme_changed.next().await {
 73                                let theme = theme?;
 74                                sender.send(Event::CursorTheme(theme))?;
 75                            }
 76                            anyhow::Ok(())
 77                        })
 78                        .detach();
 79                }
 80
 81                if let Ok(mut cursor_size_changed) = settings
 82                    .receive_setting_changed_with_args::<i32>(
 83                        "org.gnome.desktop.interface",
 84                        "cursor-size",
 85                    )
 86                    .await
 87                {
 88                    let sender = sender.clone();
 89                    background
 90                        .spawn(async move {
 91                            while let Some(size) = cursor_size_changed.next().await {
 92                                let size = size?;
 93                                sender.send(Event::CursorSize(size as u32))?;
 94                            }
 95                            anyhow::Ok(())
 96                        })
 97                        .detach();
 98                }
 99
100                if let Ok(mut button_layout_changed) = settings
101                    .receive_setting_changed_with_args(
102                        "org.gnome.desktop.wm.preferences",
103                        "button-layout",
104                    )
105                    .await
106                {
107                    let sender = sender.clone();
108                    background
109                        .spawn(async move {
110                            while let Some(layout) = button_layout_changed.next().await {
111                                let layout = layout?;
112                                sender.send(Event::ButtonLayout(layout))?;
113                            }
114                            anyhow::Ok(())
115                        })
116                        .detach();
117                }
118
119                let mut appearance_changed = settings.receive_color_scheme_changed().await?;
120                while let Some(scheme) = appearance_changed.next().await {
121                    sender.send(Event::WindowAppearance(
122                        window_appearance_from_color_scheme(scheme),
123                    ))?;
124                }
125
126                anyhow::Ok(())
127            })
128            .detach();
129
130        Self { channel }
131    }
132}
133
134impl EventSource for XDPEventSource {
135    type Event = Event;
136    type Metadata = ();
137    type Ret = ();
138    type Error = anyhow::Error;
139
140    fn process_events<F>(
141        &mut self,
142        readiness: Readiness,
143        token: Token,
144        mut callback: F,
145    ) -> Result<PostAction, Self::Error>
146    where
147        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
148    {
149        self.channel.process_events(readiness, token, |evt, _| {
150            if let calloop::channel::Event::Msg(msg) = evt {
151                (callback)(msg, &mut ())
152            }
153        })?;
154
155        Ok(PostAction::Continue)
156    }
157
158    fn register(
159        &mut self,
160        poll: &mut Poll,
161        token_factory: &mut TokenFactory,
162    ) -> calloop::Result<()> {
163        self.channel.register(poll, token_factory)?;
164
165        Ok(())
166    }
167
168    fn reregister(
169        &mut self,
170        poll: &mut Poll,
171        token_factory: &mut TokenFactory,
172    ) -> calloop::Result<()> {
173        self.channel.reregister(poll, token_factory)?;
174
175        Ok(())
176    }
177
178    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
179        self.channel.unregister(poll)?;
180
181        Ok(())
182    }
183}
184
185fn window_appearance_from_color_scheme(cs: ColorScheme) -> WindowAppearance {
186    match cs {
187        ColorScheme::PreferDark => WindowAppearance::Dark,
188        ColorScheme::PreferLight => WindowAppearance::Light,
189        ColorScheme::NoPreference => WindowAppearance::Light,
190    }
191}