1use {
5 crate::ApplicationExt,
6 iced::Subscription,
7 iced_futures::futures::{
8 SinkExt,
9 channel::mpsc::{Receiver, Sender},
10 },
11 std::{any::TypeId, collections::HashMap},
12 url::Url,
13 zbus::{interface, proxy, zvariant::Value},
14};
15
16#[cold]
17pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Message>> {
18 use iced_futures::futures::StreamExt;
19 iced_futures::Subscription::run_with(TypeId::of::<DbusActivation>(), |_| {
20 iced::stream::channel(
21 10,
22 move |mut output: Sender<crate::Action<App::Message>>| async move {
23 let mut single_instance: DbusActivation = DbusActivation::new();
24 let mut rx = single_instance.rx();
25 if let Ok(builder) = zbus::connection::Builder::session() {
26 let path: String = format!("/{}", App::APP_ID.replace('.', "/"));
27 if let Ok(conn) = builder.build().await {
28 if conn.object_server().at(path, single_instance).await != Ok(true) {
40 tracing::error!("Failed to serve dbus");
41 std::process::exit(1);
42 }
43 if conn.request_name(App::APP_ID).await.is_err() {
44 tracing::error!("Failed to serve dbus");
45 std::process::exit(1);
46 }
47
48 output
49 .send(crate::Action::Cosmic(crate::app::Action::DbusConnection(
50 conn.clone(),
51 )))
52 .await;
53
54 #[cfg(feature = "smol")]
55 let handle = {
56 std::thread::spawn(move || {
57 let conn_clone = _conn.clone();
58
59 zbus::block_on(async move {
60 loop {
61 conn_clone.executor().tick().await;
62 }
63 })
64 })
65 };
66 while let Some(mut msg) = rx.next().await {
67 if let Some(token) = msg.activation_token.take() {
68 if let Err(err) = output
69 .send(crate::Action::Cosmic(crate::app::Action::Activate(
70 token,
71 )))
72 .await
73 {
74 tracing::error!(?err, "Failed to send message");
75 }
76 }
77 if let Err(err) = output.send(crate::Action::DbusActivation(msg)).await
78 {
79 tracing::error!(?err, "Failed to send message");
80 }
81 }
82 }
83 } else {
84 tracing::warn!("Failed to connect to dbus for single instance");
85 }
86
87 loop {
88 iced::futures::pending!();
89 }
90 },
91 )
92 })
93}
94
95#[derive(Debug, Clone)]
96pub struct Message<Action = String, Args = Vec<String>> {
97 pub activation_token: Option<String>,
98 pub desktop_startup_id: Option<String>,
99 pub msg: Details<Action, Args>,
100}
101
102#[derive(Debug, Clone)]
103pub enum Details<Action = String, Args = Vec<String>> {
104 Activate,
105 Open {
106 url: Vec<Url>,
107 },
108 ActivateAction {
110 action: Action,
111 args: Args,
112 },
113}
114
115#[derive(Debug, Default)]
116pub struct DbusActivation(Option<Sender<Message>>);
117
118impl DbusActivation {
119 #[must_use]
120 #[inline]
121 pub fn new() -> Self {
122 Self(None)
123 }
124
125 #[inline]
126 pub fn rx(&mut self) -> Receiver<Message> {
127 let (tx, rx) = iced_futures::futures::channel::mpsc::channel(10);
128 self.0 = Some(tx);
129 rx
130 }
131}
132
133#[proxy(interface = "org.freedesktop.DbusActivation", assume_defaults = true)]
134pub trait DbusActivationInterface {
135 fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) -> zbus::Result<()>;
137
138 fn open(
140 &mut self,
141 uris: Vec<&str>,
142 platform_data: HashMap<&str, Value<'_>>,
143 ) -> zbus::Result<()>;
144
145 fn activate_action(
147 &mut self,
148 action_name: &str,
149 parameter: Vec<&str>,
150 platform_data: HashMap<&str, Value<'_>>,
151 ) -> zbus::Result<()>;
152}
153
154#[interface(name = "org.freedesktop.DbusActivation")]
155impl DbusActivation {
156 #[cold]
157 async fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) {
158 if let Some(tx) = &mut self.0 {
159 let _ = tx
160 .send(Message {
161 activation_token: platform_data.get("activation-token").and_then(|t| match t {
162 Value::Str(t) => Some(t.to_string()),
163 _ => None,
164 }),
165 desktop_startup_id: platform_data.get("desktop-startup-id").and_then(
166 |t| match t {
167 Value::Str(t) => Some(t.to_string()),
168 _ => None,
169 },
170 ),
171 msg: Details::Activate,
172 })
173 .await;
174 }
175 }
176
177 #[cold]
178 async fn open(&mut self, uris: Vec<&str>, platform_data: HashMap<&str, Value<'_>>) {
179 if let Some(tx) = &mut self.0 {
180 let _ = tx
181 .send(Message {
182 activation_token: platform_data.get("activation-token").and_then(|t| match t {
183 Value::Str(t) => Some(t.to_string()),
184 _ => None,
185 }),
186 desktop_startup_id: platform_data.get("desktop-startup-id").and_then(
187 |t| match t {
188 Value::Str(t) => Some(t.to_string()),
189 _ => None,
190 },
191 ),
192 msg: Details::Open {
193 url: uris.iter().filter_map(|u| Url::parse(u).ok()).collect(),
194 },
195 })
196 .await;
197 }
198 }
199
200 #[cold]
201 async fn activate_action(
202 &mut self,
203 action_name: &str,
204 parameter: Vec<&str>,
205 platform_data: HashMap<&str, Value<'_>>,
206 ) {
207 if let Some(tx) = &mut self.0 {
208 let _ = tx
209 .send(Message {
210 activation_token: platform_data.get("activation-token").and_then(|t| match t {
211 Value::Str(t) => Some(t.to_string()),
212 _ => None,
213 }),
214 desktop_startup_id: platform_data.get("desktop-startup-id").and_then(
215 |t| match t {
216 Value::Str(t) => Some(t.to_string()),
217 _ => None,
218 },
219 ),
220 msg: Details::ActivateAction {
221 action: action_name.to_string(),
222 args: parameter
223 .iter()
224 .map(std::string::ToString::to_string)
225 .collect(),
226 },
227 })
228 .await;
229 }
230 }
231}