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}