veridian_kernel/services/notification_ipc.rs
1//! Notification IPC Service
2//!
3//! Provides an IPC endpoint for desktop notification delivery. User-space
4//! applications send notification messages to the well-known notification
5//! endpoint, and this service dispatches them to the desktop notification
6//! manager for rendering as toast popups.
7
8#![allow(dead_code)]
9
10use alloc::string::String;
11use core::sync::atomic::{AtomicBool, Ordering};
12
13use crate::{
14 desktop::notification::{self, NotificationUrgency},
15 error::KernelError,
16 services::desktop_ipc::DESKTOP_NOTIFICATION_ENDPOINT,
17};
18
19// ---------------------------------------------------------------------------
20// Message types
21// ---------------------------------------------------------------------------
22
23/// Type of notification IPC message.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[repr(u8)]
26pub enum NotificationMessageType {
27 /// Post a new notification (returns notification ID).
28 Notify = 0,
29 /// Dismiss a specific notification by ID.
30 Dismiss = 1,
31 /// Dismiss all active notifications.
32 DismissAll = 2,
33 /// Query the count of active notifications.
34 GetActive = 3,
35}
36
37impl NotificationMessageType {
38 /// Convert a raw u8 to a message type, if valid.
39 pub fn from_u8(v: u8) -> Option<Self> {
40 match v {
41 0 => Some(Self::Notify),
42 1 => Some(Self::Dismiss),
43 2 => Some(Self::DismissAll),
44 3 => Some(Self::GetActive),
45 _ => None,
46 }
47 }
48}
49
50/// A notification IPC message sent from user-space to the notification service.
51#[derive(Debug, Clone)]
52pub struct NotificationMessage {
53 /// The type of operation requested.
54 pub msg_type: NotificationMessageType,
55 /// Notification summary / title (used by `Notify`).
56 pub summary: String,
57 /// Notification body text (used by `Notify`).
58 pub body: String,
59 /// Urgency level: 0 = Low, 1 = Normal, 2 = Critical (used by `Notify`).
60 pub urgency: u8,
61 /// Name of the sending application (used by `Notify`).
62 pub app_name: String,
63 /// Notification ID (used by `Dismiss`).
64 pub notification_id: u32,
65}
66
67impl NotificationMessage {
68 /// Create a `Notify` message.
69 pub fn new_notify(summary: &str, body: &str, urgency: u8, app_name: &str) -> Self {
70 Self {
71 msg_type: NotificationMessageType::Notify,
72 summary: String::from(summary),
73 body: String::from(body),
74 urgency,
75 app_name: String::from(app_name),
76 notification_id: 0,
77 }
78 }
79
80 /// Create a `Dismiss` message for a specific notification.
81 pub fn new_dismiss(id: u32) -> Self {
82 Self {
83 msg_type: NotificationMessageType::Dismiss,
84 summary: String::new(),
85 body: String::new(),
86 urgency: 1,
87 app_name: String::new(),
88 notification_id: id,
89 }
90 }
91
92 /// Create a `DismissAll` message.
93 pub fn new_dismiss_all() -> Self {
94 Self {
95 msg_type: NotificationMessageType::DismissAll,
96 summary: String::new(),
97 body: String::new(),
98 urgency: 1,
99 app_name: String::new(),
100 notification_id: 0,
101 }
102 }
103
104 /// Create a `GetActive` query message.
105 pub fn new_get_active() -> Self {
106 Self {
107 msg_type: NotificationMessageType::GetActive,
108 summary: String::new(),
109 body: String::new(),
110 urgency: 1,
111 app_name: String::new(),
112 notification_id: 0,
113 }
114 }
115}
116
117// ---------------------------------------------------------------------------
118// IPC Server
119// ---------------------------------------------------------------------------
120
121/// Whether the notification IPC server has been initialized.
122static INITIALIZED: AtomicBool = AtomicBool::new(false);
123
124/// Notification IPC server that handles incoming notification messages
125/// and dispatches them to the desktop notification manager.
126pub struct NotificationIpcServer {
127 /// The well-known endpoint ID this server is bound to.
128 endpoint_id: u64,
129}
130
131impl NotificationIpcServer {
132 /// Create a new notification IPC server instance.
133 pub fn new() -> Self {
134 Self {
135 endpoint_id: DESKTOP_NOTIFICATION_ENDPOINT,
136 }
137 }
138
139 /// Initialize the notification IPC server by registering the endpoint.
140 ///
141 /// The actual endpoint registration is handled by `desktop_ipc::init()`;
142 /// this method validates that the endpoint is available and marks the
143 /// notification service as ready to accept messages.
144 pub fn init(&self) -> Result<(), KernelError> {
145 if INITIALIZED.load(Ordering::Acquire) {
146 return Err(KernelError::InvalidState {
147 expected: "uninitialized",
148 actual: "initialized",
149 });
150 }
151
152 crate::println!(
153 "[NOTIFY-IPC] Notification IPC server bound to endpoint {}",
154 self.endpoint_id
155 );
156
157 INITIALIZED.store(true, Ordering::Release);
158 Ok(())
159 }
160
161 /// Handle an incoming notification message.
162 ///
163 /// Returns:
164 /// - For `Notify`: the assigned notification ID.
165 /// - For `Dismiss`/`DismissAll`: 0 on success.
166 /// - For `GetActive`: the count of active notifications.
167 pub fn handle_message(&self, msg: &NotificationMessage) -> Result<u32, KernelError> {
168 match msg.msg_type {
169 NotificationMessageType::Notify => {
170 let urgency = NotificationUrgency::from_u8(msg.urgency);
171 let id = notification::with_notification_manager(|mgr| {
172 mgr.notify(
173 msg.summary.clone(),
174 msg.body.clone(),
175 urgency,
176 msg.app_name.clone(),
177 )
178 })
179 .ok_or(KernelError::InvalidState {
180 expected: "notification_manager_initialized",
181 actual: "not_initialized",
182 })?;
183
184 Ok(id)
185 }
186
187 NotificationMessageType::Dismiss => {
188 notification::dismiss(msg.notification_id);
189 Ok(0)
190 }
191
192 NotificationMessageType::DismissAll => {
193 notification::dismiss_all();
194 Ok(0)
195 }
196
197 NotificationMessageType::GetActive => {
198 let count =
199 notification::with_notification_manager(|mgr| mgr.active_count()).unwrap_or(0);
200 Ok(count as u32)
201 }
202 }
203 }
204}
205
206impl Default for NotificationIpcServer {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212// ---------------------------------------------------------------------------
213// Module-level initialization
214// ---------------------------------------------------------------------------
215
216/// Initialize the notification IPC service.
217pub fn init() -> Result<(), KernelError> {
218 let server = NotificationIpcServer::new();
219 server.init()?;
220 Ok(())
221}
222
223/// Check whether the notification IPC service has been initialized.
224pub fn is_initialized() -> bool {
225 INITIALIZED.load(Ordering::Acquire)
226}