⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

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}