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}