1#[cfg(feature = "dbus")]
6use dbus::ffidisp::Connection as DbusConnection;
7#[cfg(feature = "zbus")]
8use zbus::{block_on, zvariant};
9
10use crate::{error::*, notification::Notification};
11
12pub use crate::response::{
13 ActionResponse, CloseHandler, CloseReason, NotificationResponse,
14 ResponseHandler as ActionResponseHandler,
15};
16
17use std::ops::{Deref, DerefMut};
18
19#[cfg(feature = "dbus")]
20mod dbus_rs;
21#[cfg(all(feature = "dbus", not(feature = "zbus")))]
22use dbus_rs::bus;
23
24#[cfg(feature = "zbus")]
25mod zbus_rs;
26#[cfg(all(feature = "zbus", not(feature = "dbus")))]
27use zbus_rs::bus;
28
29#[cfg(all(feature = "dbus", feature = "zbus"))]
30mod bus;
31
32#[cfg(not(feature = "debug_namespace"))]
42#[doc(hidden)]
43pub static NOTIFICATION_DEFAULT_BUS: &str = "org.freedesktop.Notifications";
44
45#[cfg(feature = "debug_namespace")]
46#[doc(hidden)]
47pub static NOTIFICATION_DEFAULT_BUS: &str = "de.hoodie.Notifications";
49
50#[doc(hidden)]
51pub static NOTIFICATION_INTERFACE: &str = "org.freedesktop.Notifications";
52
53#[doc(hidden)]
54pub static NOTIFICATION_OBJECTPATH: &str = "/org/freedesktop/Notifications";
55
56pub(crate) use bus::NotificationBus;
57
58#[derive(Debug)]
59enum NotificationHandleInner {
60 #[cfg(feature = "dbus")]
61 Dbus(dbus_rs::DbusNotificationHandle),
62
63 #[cfg(feature = "zbus")]
64 Zbus(zbus_rs::ZbusNotificationHandle),
65}
66
67#[derive(Debug)]
71pub struct NotificationHandle {
72 inner: NotificationHandleInner,
73}
74
75#[allow(dead_code)]
76impl NotificationHandle {
77 #[cfg(feature = "dbus")]
78 pub(crate) fn for_dbus(
79 id: u32,
80 connection: DbusConnection,
81 notification: Notification,
82 ) -> NotificationHandle {
83 NotificationHandle {
84 inner: dbus_rs::DbusNotificationHandle::new(id, connection, notification).into(),
85 }
86 }
87
88 #[cfg(feature = "zbus")]
89 pub(crate) fn for_zbus(
90 id: u32,
91 connection: zbus::Connection,
92 notification: Notification,
93 ) -> NotificationHandle {
94 NotificationHandle {
95 inner: zbus_rs::ZbusNotificationHandle::new(id, connection, notification).into(),
96 }
97 }
98
99 pub fn wait_for_action<F>(self, invocation_closure: F)
102 where
103 F: FnOnce(&str),
104 {
105 match self.inner {
106 #[cfg(feature = "dbus")]
107 NotificationHandleInner::Dbus(inner) => {
108 let _ = inner.wait_for_action(|response: &NotificationResponse| match response {
109 NotificationResponse::Default => invocation_closure("default"),
110 NotificationResponse::Action(ref action) => invocation_closure(action),
111 NotificationResponse::Reply(_) => { }
112 NotificationResponse::Closed(_) => invocation_closure("__closed"),
113 });
114 }
115
116 #[cfg(feature = "zbus")]
117 NotificationHandleInner::Zbus(inner) => {
118 block_on(
119 inner.wait_for_action(|response: &NotificationResponse| match response {
120 NotificationResponse::Default => invocation_closure("default"),
121 NotificationResponse::Action(ref action) => invocation_closure(action),
122 NotificationResponse::Reply(_) => { }
123 NotificationResponse::Closed(_) => invocation_closure("__closed"), }),
125 );
126 }
127 };
128 }
129
130 pub fn wait_for_response(self, handler: impl ActionResponseHandler) -> Result<()> {
135 match self.inner {
136 #[cfg(feature = "dbus")]
137 NotificationHandleInner::Dbus(inner) => inner.wait_for_action(handler),
138 #[cfg(feature = "zbus")]
139 NotificationHandleInner::Zbus(inner) => {
140 block_on(inner.wait_for_action(handler));
141 Ok(())
142 }
143 }
144 }
145
146 #[cfg(feature = "zbus")]
183 pub async fn wait_for_action_async<F>(&self, invocation_closure: F)
184 where
185 F: FnOnce(&NotificationResponse),
186 {
187 match &self.inner {
188 #[cfg(feature = "dbus")]
189 NotificationHandleInner::Dbus(_) => {
190 unimplemented!("async methods are not supported with the `dbus` backend");
191 }
192 #[cfg(feature = "zbus")]
193 NotificationHandleInner::Zbus(inner) => inner.wait_for_action(invocation_closure).await,
194 }
195 }
196
197 pub fn close(self) {
215 match self.inner {
216 #[cfg(feature = "dbus")]
217 NotificationHandleInner::Dbus(inner) => inner.close(),
218 #[cfg(feature = "zbus")]
219 NotificationHandleInner::Zbus(inner) => block_on(inner.close()),
220 }
221 }
222
223 #[cfg(feature = "zbus")]
229 pub async fn close_async(&self) {
230 match &self.inner {
231 #[cfg(feature = "dbus")]
232 NotificationHandleInner::Dbus(_) => {
233 unimplemented!("async methods are not supported with the `dbus` backend");
234 }
235 #[cfg(feature = "zbus")]
236 NotificationHandleInner::Zbus(inner) => inner.close().await,
237 }
238 }
239
240 pub fn on_close<A>(self, handler: impl CloseHandler<A>) {
270 match self.inner {
271 #[cfg(feature = "dbus")]
272 NotificationHandleInner::Dbus(inner) => {
273 let _ = inner.wait_for_action(|action: &NotificationResponse| {
274 if let NotificationResponse::Closed(reason) = action {
275 handler.call(*reason);
276 }
277 });
278 }
279 #[cfg(feature = "zbus")]
280 NotificationHandleInner::Zbus(inner) => {
281 block_on(inner.wait_for_action(|action: &NotificationResponse| {
282 if let NotificationResponse::Closed(reason) = action {
283 handler.call(*reason);
284 }
285 }));
286 }
287 };
288 }
289
290 pub fn update(&mut self) -> Result<()> {
311 match self.inner {
312 #[cfg(feature = "dbus")]
313 NotificationHandleInner::Dbus(ref mut inner) => inner.update(),
314 #[cfg(feature = "zbus")]
315 NotificationHandleInner::Zbus(ref mut inner) => inner.update(),
316 }
317 }
318
319 pub fn id(&self) -> u32 {
321 match self.inner {
322 #[cfg(feature = "dbus")]
323 NotificationHandleInner::Dbus(ref inner) => inner.id,
324 #[cfg(feature = "zbus")]
325 NotificationHandleInner::Zbus(ref inner) => inner.id,
326 }
327 }
328}
329
330impl Deref for NotificationHandle {
332 type Target = Notification;
333
334 fn deref(&self) -> &Notification {
335 match self.inner {
336 #[cfg(feature = "dbus")]
337 NotificationHandleInner::Dbus(ref inner) => &inner.notification,
338 #[cfg(feature = "zbus")]
339 NotificationHandleInner::Zbus(ref inner) => &inner.notification,
340 }
341 }
342}
343
344impl DerefMut for NotificationHandle {
346 fn deref_mut(&mut self) -> &mut Notification {
347 match self.inner {
348 #[cfg(feature = "dbus")]
349 NotificationHandleInner::Dbus(ref mut inner) => &mut inner.notification,
350 #[cfg(feature = "zbus")]
351 NotificationHandleInner::Zbus(ref mut inner) => &mut inner.notification,
352 }
353 }
354}
355
356#[cfg(feature = "dbus")]
357impl From<dbus_rs::DbusNotificationHandle> for NotificationHandleInner {
358 fn from(handle: dbus_rs::DbusNotificationHandle) -> NotificationHandleInner {
359 NotificationHandleInner::Dbus(handle)
360 }
361}
362
363#[cfg(feature = "zbus")]
364impl From<zbus_rs::ZbusNotificationHandle> for NotificationHandleInner {
365 fn from(handle: zbus_rs::ZbusNotificationHandle) -> NotificationHandleInner {
366 NotificationHandleInner::Zbus(handle)
367 }
368}
369
370#[cfg(feature = "dbus")]
371impl From<dbus_rs::DbusNotificationHandle> for NotificationHandle {
372 fn from(handle: dbus_rs::DbusNotificationHandle) -> NotificationHandle {
373 NotificationHandle {
374 inner: handle.into(),
375 }
376 }
377}
378
379#[cfg(feature = "zbus")]
380impl From<zbus_rs::ZbusNotificationHandle> for NotificationHandle {
381 fn from(handle: zbus_rs::ZbusNotificationHandle) -> NotificationHandle {
382 NotificationHandle {
383 inner: handle.into(),
384 }
385 }
386}
387
388#[cfg(all(
395 not(any(feature = "dbus", feature = "zbus")),
396 unix,
397 not(target_os = "macos")
398))]
399compile_error!("you have to build with either zbus or dbus turned on");
400
401#[derive(Copy, Clone, Debug)]
403pub enum DbusStack {
404 Dbus,
406 Zbus,
408}
409
410#[cfg(all(feature = "dbus", feature = "zbus"))]
411const DBUS_SWITCH_VAR: &str = "DBUSRS";
412
413#[cfg(all(feature = "zbus", not(feature = "dbus")))]
414pub(crate) fn show_notification(notification: &Notification) -> Result<NotificationHandle> {
415 block_on(zbus_rs::connect_and_send_notification(notification)).map(Into::into)
416}
417
418#[cfg(feature = "zbus")]
419pub(crate) async fn show_notification_async(
420 notification: &Notification,
421) -> Result<NotificationHandle> {
422 zbus_rs::connect_and_send_notification(notification)
423 .await
424 .map(Into::into)
425}
426
427#[cfg(feature = "zbus")]
428pub(crate) async fn show_notification_async_at_bus(
429 notification: &Notification,
430 bus: NotificationBus,
431) -> Result<NotificationHandle> {
432 zbus_rs::connect_and_send_notification_at_bus(notification, bus)
433 .await
434 .map(Into::into)
435}
436
437#[cfg(all(feature = "dbus", not(feature = "zbus")))]
438pub(crate) fn show_notification(notification: &Notification) -> Result<NotificationHandle> {
439 dbus_rs::connect_and_send_notification(notification).map(Into::into)
440}
441
442#[cfg(all(feature = "dbus", feature = "zbus"))]
443pub(crate) fn show_notification(notification: &Notification) -> Result<NotificationHandle> {
444 if std::env::var(DBUS_SWITCH_VAR).is_ok() {
445 dbus_rs::connect_and_send_notification(notification).map(Into::into)
446 } else {
447 block_on(zbus_rs::connect_and_send_notification(notification)).map(Into::into)
448 }
449}
450
451#[cfg(all(feature = "zbus", not(feature = "dbus")))]
455pub fn dbus_stack() -> Option<DbusStack> {
456 Some(DbusStack::Zbus)
457}
458
459#[cfg(all(feature = "dbus", not(feature = "zbus")))]
463pub fn dbus_stack() -> Option<DbusStack> {
464 Some(DbusStack::Dbus)
465}
466
467#[cfg(all(feature = "dbus", feature = "zbus"))]
471pub fn dbus_stack() -> Option<DbusStack> {
472 Some(if std::env::var(DBUS_SWITCH_VAR).is_ok() {
473 DbusStack::Dbus
474 } else {
475 DbusStack::Zbus
476 })
477}
478
479#[cfg(all(not(feature = "dbus"), not(feature = "zbus")))]
483pub fn dbus_stack() -> Option<DbusStack> {
484 None
485}
486
487#[cfg(all(feature = "zbus", not(feature = "dbus")))]
491pub fn get_capabilities() -> Result<Vec<String>> {
492 block_on(zbus_rs::get_capabilities())
493}
494
495#[cfg(all(feature = "dbus", not(feature = "zbus")))]
499pub fn get_capabilities() -> Result<Vec<String>> {
500 dbus_rs::get_capabilities()
501}
502
503#[cfg(all(feature = "dbus", feature = "zbus"))]
507pub fn get_capabilities() -> Result<Vec<String>> {
508 if std::env::var(DBUS_SWITCH_VAR).is_ok() {
509 dbus_rs::get_capabilities()
510 } else {
511 block_on(zbus_rs::get_capabilities())
512 }
513}
514
515#[cfg(all(feature = "zbus", not(feature = "dbus")))]
521pub fn get_server_information() -> Result<ServerInformation> {
522 block_on(zbus_rs::get_server_information())
523}
524
525#[cfg(all(feature = "dbus", not(feature = "zbus")))]
531pub fn get_server_information() -> Result<ServerInformation> {
532 dbus_rs::get_server_information()
533}
534
535#[cfg(all(feature = "dbus", feature = "zbus"))]
541pub fn get_server_information() -> Result<ServerInformation> {
542 if std::env::var(DBUS_SWITCH_VAR).is_ok() {
543 dbus_rs::get_server_information()
544 } else {
545 block_on(zbus_rs::get_server_information())
546 }
547}
548
549#[derive(Debug)]
551#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
552#[cfg_attr(feature = "zbus", derive(zvariant::Type))]
553pub struct ServerInformation {
554 pub name: String,
556 pub vendor: String,
558 pub version: String,
560 pub spec_version: String,
562}
563
564#[cfg(all(feature = "zbus", not(feature = "dbus")))]
579pub fn handle_action<F>(id: u32, func: F) -> Result<()>
581where
582 F: FnOnce(&ActionResponse<'_>),
583{
584 block_on(zbus_rs::handle_action(id, action_response_adapter(func)));
585 Ok(())
586}
587
588#[cfg(all(feature = "dbus", not(feature = "zbus")))]
593pub fn handle_action<F>(id: u32, func: F) -> Result<()>
595where
596 F: FnOnce(&ActionResponse<'_>),
597{
598 dbus_rs::handle_action(id, action_response_adapter(func))
599}
600
601#[cfg(all(feature = "dbus", feature = "zbus"))]
606pub fn handle_action<F>(id: u32, func: F) -> Result<()>
608where
609 F: FnOnce(&ActionResponse<'_>),
610{
611 if std::env::var(DBUS_SWITCH_VAR).is_ok() {
612 dbus_rs::handle_action(id, action_response_adapter(func))
613 } else {
614 block_on(zbus_rs::handle_action(id, action_response_adapter(func)));
615 Ok(())
616 }
617}
618
619fn action_response_adapter<F>(func: F) -> impl FnOnce(&NotificationResponse)
622where
623 F: FnOnce(&ActionResponse<'_>),
624{
625 move |response: &NotificationResponse| match response {
626 NotificationResponse::Default => func(&ActionResponse::Custom("default")),
627 NotificationResponse::Action(ref s) => func(&ActionResponse::Custom(s.as_str())),
628 NotificationResponse::Reply(_) => { }
629 NotificationResponse::Closed(r) => func(&ActionResponse::Closed(*r)),
630 }
631}