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