wip: send dismiss requests and display notifications
This commit is contained in:
parent
c4566730ab
commit
060b316365
5 changed files with 330 additions and 15 deletions
|
|
@ -6,15 +6,16 @@ license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
icon-loader = { version = "0.3.6", features = ["gtk"] }
|
|
||||||
libcosmic.workspace = true
|
libcosmic.workspace = true
|
||||||
cosmic-time.workspace = true
|
cosmic-time.workspace = true
|
||||||
cosmic-applet = { path = "../applet" }
|
cosmic-applet = { path = "../applet" }
|
||||||
nix = "0.26"
|
nix = "0.26"
|
||||||
tokio = { version = "1.24.1", features = ["sync", "rt", "tracing", "macros", "net", "io-util", "io-std"] }
|
tokio = { version = "1.24.1", features = ["sync", "rt", "tracing", "macros", "net", "io-util", "io-std"] }
|
||||||
cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" }
|
cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" }
|
||||||
|
cosmic-notifications-config = { git = "https://github.com/pop-os/cosmic-notifications" }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
sendfd = { version = "0.4", features = [ "tokio" ] }
|
sendfd = { version = "0.4", features = [ "tokio" ] }
|
||||||
bytemuck = "1"
|
bytemuck = "1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
zbus = "3.14"
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,32 @@
|
||||||
mod subscriptions;
|
mod subscriptions;
|
||||||
|
|
||||||
|
use cosmic::cosmic_config::{config_subscription, Config, CosmicConfigEntry};
|
||||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
widget::{button, column, row, text, Row, Space},
|
widget::{button, column, row, text, Row, Space},
|
||||||
window, Alignment, Application, Color, Command, Length, Subscription,
|
window, Alignment, Application, Color, Command, Length, Subscription,
|
||||||
};
|
};
|
||||||
|
use cosmic::iced_core::image;
|
||||||
|
use cosmic::iced_widget::button::StyleSheet;
|
||||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
||||||
|
|
||||||
use cosmic::iced_style::application::{self, Appearance};
|
use cosmic::iced_style::application::{self, Appearance};
|
||||||
|
|
||||||
use cosmic::iced_widget::Button;
|
use cosmic::iced_widget::{horizontal_space, scrollable, Column};
|
||||||
use cosmic::theme::Svg;
|
use cosmic::theme::{Button, Svg};
|
||||||
use cosmic::widget::{divider, icon};
|
use cosmic::widget::{divider, icon};
|
||||||
use cosmic::Renderer;
|
use cosmic::Renderer;
|
||||||
use cosmic::{Element, Theme};
|
use cosmic::{Element, Theme};
|
||||||
|
use cosmic_notifications_config::NotificationsConfig;
|
||||||
|
use cosmic_notifications_util::{AppletEvent, Notification};
|
||||||
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
|
||||||
use cosmic_notifications_util::AppletEvent;
|
use std::borrow::Cow;
|
||||||
use tracing::info;
|
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
pub fn main() -> cosmic::iced::Result {
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
pub async fn main() -> cosmic::iced::Result {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
info!("Notifications applet");
|
info!("Notifications applet");
|
||||||
|
|
@ -34,12 +41,14 @@ static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
struct Notifications {
|
struct Notifications {
|
||||||
applet_helper: CosmicAppletHelper,
|
applet_helper: CosmicAppletHelper,
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
config: NotificationsConfig,
|
||||||
|
config_helper: Option<Config>,
|
||||||
icon_name: String,
|
icon_name: String,
|
||||||
popup: Option<window::Id>,
|
popup: Option<window::Id>,
|
||||||
id_ctr: u128,
|
id_ctr: u128,
|
||||||
do_not_disturb: bool,
|
notifications: Vec<Notification>,
|
||||||
notifications: Vec<Vec<String>>,
|
|
||||||
timeline: Timeline,
|
timeline: Timeline,
|
||||||
|
dbus_sender: Option<Sender<subscriptions::dbus::Input>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -50,7 +59,10 @@ enum Message {
|
||||||
Ignore,
|
Ignore,
|
||||||
Frame(Instant),
|
Frame(Instant),
|
||||||
Theme(Theme),
|
Theme(Theme),
|
||||||
NotificationEvent(AppletEvent)
|
NotificationEvent(AppletEvent),
|
||||||
|
Config(NotificationsConfig),
|
||||||
|
DbusEvent(subscriptions::dbus::Output),
|
||||||
|
Dismissed(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Notifications {
|
impl Application for Notifications {
|
||||||
|
|
@ -62,11 +74,30 @@ impl Application for Notifications {
|
||||||
fn new(_flags: ()) -> (Notifications, Command<Message>) {
|
fn new(_flags: ()) -> (Notifications, Command<Message>) {
|
||||||
let applet_helper = CosmicAppletHelper::default();
|
let applet_helper = CosmicAppletHelper::default();
|
||||||
let theme = applet_helper.theme();
|
let theme = applet_helper.theme();
|
||||||
|
let helper = Config::new(
|
||||||
|
cosmic_notifications_config::ID,
|
||||||
|
NotificationsConfig::version(),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let config: NotificationsConfig = helper
|
||||||
|
.as_ref()
|
||||||
|
.map(|helper| {
|
||||||
|
NotificationsConfig::get_entry(helper).unwrap_or_else(|(errors, config)| {
|
||||||
|
for err in errors {
|
||||||
|
tracing::error!("{:?}", err);
|
||||||
|
}
|
||||||
|
config
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
(
|
(
|
||||||
Notifications {
|
Notifications {
|
||||||
applet_helper,
|
applet_helper,
|
||||||
theme,
|
theme,
|
||||||
icon_name: "notification-alert-symbolic".to_string(),
|
icon_name: "notification-alert-symbolic".to_string(),
|
||||||
|
config_helper: helper,
|
||||||
|
config,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Command::none(),
|
Command::none(),
|
||||||
|
|
@ -95,10 +126,25 @@ impl Application for Notifications {
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
Subscription::batch(vec![
|
Subscription::batch(vec![
|
||||||
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
self.applet_helper.theme_subscription(0).map(Message::Theme),
|
||||||
|
config_subscription::<u64, NotificationsConfig>(
|
||||||
|
0,
|
||||||
|
cosmic_notifications_config::ID.into(),
|
||||||
|
NotificationsConfig::version(),
|
||||||
|
)
|
||||||
|
.map(|(_, res)| match res {
|
||||||
|
Ok(config) => Message::Config(config),
|
||||||
|
Err((errors, config)) => {
|
||||||
|
for err in errors {
|
||||||
|
tracing::error!("{:?}", err);
|
||||||
|
}
|
||||||
|
Message::Config(config)
|
||||||
|
}
|
||||||
|
}),
|
||||||
self.timeline
|
self.timeline
|
||||||
.as_subscription()
|
.as_subscription()
|
||||||
.map(|(_, now)| Message::Frame(now)),
|
.map(|(_, now)| Message::Frame(now)),
|
||||||
subscriptions::notifications::notifications().map(Message::NotificationEvent)
|
subscriptions::dbus::proxy().map(Message::DbusEvent),
|
||||||
|
subscriptions::notifications::notifications().map(Message::NotificationEvent),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +178,12 @@ impl Application for Notifications {
|
||||||
}
|
}
|
||||||
Message::DoNotDisturb(chain, b) => {
|
Message::DoNotDisturb(chain, b) => {
|
||||||
self.timeline.set_chain(chain).start();
|
self.timeline.set_chain(chain).start();
|
||||||
self.do_not_disturb = b;
|
self.config.do_not_disturb = b;
|
||||||
|
if let Some(helper) = &self.config_helper {
|
||||||
|
if let Err(err) = self.config.write_entry(helper) {
|
||||||
|
tracing::error!("{:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
Message::Settings => {
|
Message::Settings => {
|
||||||
|
|
@ -144,6 +195,28 @@ impl Application for Notifications {
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
Message::Ignore => Command::none(),
|
Message::Ignore => Command::none(),
|
||||||
|
Message::Config(config) => {
|
||||||
|
self.config = config;
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
Message::Dismissed(id) => {
|
||||||
|
self.notifications.retain(|n| n.id != id);
|
||||||
|
if let Some(tx) = &self.dbus_sender {
|
||||||
|
let tx = tx.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Err(err) = tx.send(subscriptions::dbus::Input::Dismiss(id)).await {
|
||||||
|
tracing::error!("{:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
Message::DbusEvent(e) => match e {
|
||||||
|
subscriptions::dbus::Output::Ready(tx) => {
|
||||||
|
self.dbus_sender.replace(tx);
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,7 +231,7 @@ impl Application for Notifications {
|
||||||
DO_NOT_DISTURB,
|
DO_NOT_DISTURB,
|
||||||
&self.timeline,
|
&self.timeline,
|
||||||
String::from("Do Not Disturb"),
|
String::from("Do Not Disturb"),
|
||||||
self.do_not_disturb,
|
self.config.do_not_disturb,
|
||||||
Message::DoNotDisturb
|
Message::DoNotDisturb
|
||||||
)
|
)
|
||||||
.width(Length::Fill)]
|
.width(Length::Fill)]
|
||||||
|
|
@ -176,7 +249,103 @@ impl Application for Notifications {
|
||||||
]
|
]
|
||||||
.spacing(12)
|
.spacing(12)
|
||||||
} else {
|
} else {
|
||||||
row![text("TODO: make app worky with notifications")]
|
let mut notifs = Vec::with_capacity(self.notifications.len());
|
||||||
|
|
||||||
|
for n in &self.notifications {
|
||||||
|
let summary = text(if n.summary.len() > 24 {
|
||||||
|
Cow::from(format!(
|
||||||
|
"{:.26}...",
|
||||||
|
n.summary.lines().next().unwrap_or_default()
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Cow::from(&n.summary)
|
||||||
|
})
|
||||||
|
.size(18);
|
||||||
|
let urgency = n.urgency();
|
||||||
|
|
||||||
|
notifs.push(
|
||||||
|
cosmic::widget::button(Button::Custom {
|
||||||
|
active: Box::new(move |t| {
|
||||||
|
let style = if urgency > 1 {
|
||||||
|
Button::Primary
|
||||||
|
} else {
|
||||||
|
Button::Secondary
|
||||||
|
};
|
||||||
|
let cosmic = t.cosmic();
|
||||||
|
let mut a = t.active(&style);
|
||||||
|
a.border_radius = 8.0.into();
|
||||||
|
a.background = Some(Color::from(cosmic.bg_color()).into());
|
||||||
|
a.border_color = Color::from(cosmic.bg_divider());
|
||||||
|
a.border_width = 1.0;
|
||||||
|
a
|
||||||
|
}),
|
||||||
|
hover: Box::new(move |t| {
|
||||||
|
let style = if urgency > 1 {
|
||||||
|
Button::Primary
|
||||||
|
} else {
|
||||||
|
Button::Secondary
|
||||||
|
};
|
||||||
|
let cosmic = t.cosmic();
|
||||||
|
let mut a = t.hovered(&style);
|
||||||
|
a.border_radius = 8.0.into();
|
||||||
|
a.background = Some(Color::from(cosmic.bg_color()).into());
|
||||||
|
a.border_color = Color::from(cosmic.bg_divider());
|
||||||
|
a.border_width = 1.0;
|
||||||
|
a
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.custom(vec![column!(
|
||||||
|
match n.image() {
|
||||||
|
Some(cosmic_notifications_util::Image::File(path)) => {
|
||||||
|
row![icon(path.as_path(), 32), summary]
|
||||||
|
.spacing(8)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
}
|
||||||
|
Some(cosmic_notifications_util::Image::Name(name)) => {
|
||||||
|
row![icon(name.as_str(), 32), summary]
|
||||||
|
.spacing(8)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
}
|
||||||
|
Some(cosmic_notifications_util::Image::Data {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
data,
|
||||||
|
}) => {
|
||||||
|
let handle =
|
||||||
|
image::Handle::from_pixels(*width, *height, data.clone());
|
||||||
|
row![icon(handle, 32), summary]
|
||||||
|
.spacing(8)
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
}
|
||||||
|
None => row![summary],
|
||||||
|
},
|
||||||
|
text(if n.body.len() > 38 {
|
||||||
|
Cow::from(format!(
|
||||||
|
"{:.40}...",
|
||||||
|
n.body.lines().next().unwrap_or_default()
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Cow::from(&n.summary)
|
||||||
|
})
|
||||||
|
.size(14),
|
||||||
|
horizontal_space(Length::Fixed(300.0)),
|
||||||
|
)
|
||||||
|
.spacing(8)
|
||||||
|
.into()])
|
||||||
|
.on_press(Message::Dismissed(n.id))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
row!(scrollable(
|
||||||
|
Column::with_children(notifs)
|
||||||
|
.spacing(8)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.height(Length::Shrink),
|
||||||
|
)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.height(Length::Fixed(400.0)))
|
||||||
|
.width(Length::Shrink)
|
||||||
};
|
};
|
||||||
|
|
||||||
let main_content = column![
|
let main_content = column![
|
||||||
|
|
@ -201,7 +370,9 @@ impl Application for Notifications {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo put into libcosmic doing so will fix the row_button's boarder radius
|
// todo put into libcosmic doing so will fix the row_button's boarder radius
|
||||||
fn row_button(mut content: Vec<Element<Message>>) -> Button<Message, Renderer> {
|
fn row_button(
|
||||||
|
mut content: Vec<Element<Message>>,
|
||||||
|
) -> cosmic::iced::widget::Button<Message, Renderer> {
|
||||||
content.insert(0, Space::with_width(Length::Fixed(24.0)).into());
|
content.insert(0, Space::with_width(Length::Fixed(24.0)).into());
|
||||||
content.push(Space::with_width(Length::Fixed(24.0)).into());
|
content.push(Space::with_width(Length::Fixed(24.0)).into());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1,83 @@
|
||||||
// TODO connect as a client and send actions / dismissals
|
use crate::subscriptions::dbus_proxy::NotificationsProxy;
|
||||||
|
use cosmic::{
|
||||||
|
iced::{
|
||||||
|
futures::{self, SinkExt},
|
||||||
|
subscription,
|
||||||
|
},
|
||||||
|
iced_futures::Subscription,
|
||||||
|
};
|
||||||
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use tracing::{error, warn};
|
||||||
|
use zbus::Connection;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum State {
|
||||||
|
Ready,
|
||||||
|
WaitingForNotificationEvent(Connection, Receiver<Input>),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Input {
|
||||||
|
Dismiss(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Output {
|
||||||
|
Ready(Sender<Input>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn proxy() -> Subscription<Output> {
|
||||||
|
struct SomeWorker;
|
||||||
|
|
||||||
|
subscription::channel(
|
||||||
|
std::any::TypeId::of::<SomeWorker>(),
|
||||||
|
50,
|
||||||
|
|mut output| async move {
|
||||||
|
let mut state = State::Ready;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match &mut state {
|
||||||
|
State::Ready => {
|
||||||
|
let (sender, receiver) = channel(10);
|
||||||
|
let Ok(conn) = Connection::session().await else {
|
||||||
|
error!("Failed to connect to session bus");
|
||||||
|
state = State::Finished;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Err(err) = output.send(Output::Ready(sender)).await {
|
||||||
|
error!("Failed to send sender: {}", err);
|
||||||
|
state = State::Finished;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = State::WaitingForNotificationEvent(conn, receiver);
|
||||||
|
}
|
||||||
|
State::WaitingForNotificationEvent(conn, rx) => {
|
||||||
|
let Ok(proxy) = NotificationsProxy::new(&conn).await else {
|
||||||
|
error!("Failed to create proxy from session connection");
|
||||||
|
state = State::Finished;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
match rx.recv().await {
|
||||||
|
Some(Input::Dismiss(id)) => {
|
||||||
|
if let Err(err) = proxy.close_notification(id).await {
|
||||||
|
error!("Failed to close notification: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!("Notification event channel closed");
|
||||||
|
state = State::Finished;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Finished => {
|
||||||
|
let () = futures::future::pending().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
59
cosmic-applet-notifications/src/subscriptions/dbus_proxy.rs
Normal file
59
cosmic-applet-notifications/src/subscriptions/dbus_proxy.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
//! # DBus interface proxy for: `org.freedesktop.Notifications`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `3.1.1` from DBus introspection data.
|
||||||
|
//! Source: `Interface '/org/freedesktop/Notifications' from service 'org.freedesktop.Notifications' on session bus`.
|
||||||
|
//!
|
||||||
|
//! You may prefer to adapt it, instead of using it verbatim.
|
||||||
|
//!
|
||||||
|
//! More information can be found in the
|
||||||
|
//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html)
|
||||||
|
//! section of the zbus documentation.
|
||||||
|
//!
|
||||||
|
//! This DBus object implements
|
||||||
|
//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html),
|
||||||
|
//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used:
|
||||||
|
//!
|
||||||
|
//! * [`zbus::fdo::PeerProxy`]
|
||||||
|
//! * [`zbus::fdo::IntrospectableProxy`]
|
||||||
|
//! * [`zbus::fdo::PropertiesProxy`]
|
||||||
|
//!
|
||||||
|
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
|
||||||
|
use zbus::dbus_proxy;
|
||||||
|
|
||||||
|
#[dbus_proxy(
|
||||||
|
interface = "org.freedesktop.Notifications",
|
||||||
|
default_service = "org.freedesktop.Notifications",
|
||||||
|
default_path = "/org/freedesktop/Notifications"
|
||||||
|
)]
|
||||||
|
trait Notifications {
|
||||||
|
/// CloseNotification method
|
||||||
|
fn close_notification(&self, id: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// GetCapabilities method
|
||||||
|
fn get_capabilities(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// GetServerInformation method
|
||||||
|
fn get_server_information(&self) -> zbus::Result<(String, String, String, String)>;
|
||||||
|
|
||||||
|
/// Notify method
|
||||||
|
fn notify(
|
||||||
|
&self,
|
||||||
|
app_name: &str,
|
||||||
|
replaces_id: u32,
|
||||||
|
app_icon: &str,
|
||||||
|
summary: &str,
|
||||||
|
body: &str,
|
||||||
|
actions: &[&str],
|
||||||
|
hints: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
|
||||||
|
expire_timeout: i32,
|
||||||
|
) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// ActionInvoked signal
|
||||||
|
#[dbus_proxy(signal)]
|
||||||
|
fn action_invoked(&self, id: u32, action_key: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// NotificationClosed signal
|
||||||
|
#[dbus_proxy(signal)]
|
||||||
|
fn notification_closed(&self, id: u32, reason: u32) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
|
pub mod dbus;
|
||||||
|
mod dbus_proxy;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue