Skip to main content

notify_icon/
lib.rs

1//! # notify-icon-rs
2//!
3//! A safe, ergonomic Rust wrapper around the Windows `Shell_NotifyIcon` API for
4//! managing system tray icons on Windows platforms.
5//!
6//! This library provides a high-level interface to create, modify, and manage
7//! notification icons in the Windows system tray. It handles the complexities
8//! of the underlying Windows API while providing a builder pattern for easy
9//! configuration and Rust-style error handling.
10//!
11//! ## Features
12//!
13//! - **Safe wrapper**: Memory-safe abstraction over the raw Windows API
14//! - **Builder pattern**: Fluent, chainable API for configuring notification
15//!   icons
16//! - **Error handling**: Proper Rust [`Result`] types instead of raw Windows
17//!   error codes
18//! - **UTF-16 handling**: Automatic conversion of Rust strings to
19//!   Windows-compatible UTF-16
20//! - **Version support**: Support for different Windows notification icon
21//!   interface versions
22//! - **Comprehensive functionality**: Support for tooltips, balloon
23//!   notifications, GUIDs, and more
24//!
25//! ## Supported Windows Versions
26//!
27//! This library should supports Windows 95 and later versions, with enhanced
28//! functionality on:
29//! - Windows Vista and later (improved balloon notification behavior)
30//! - Windows 7 and later (additional notification features)
31//!
32//! ## Platform Requirements
33//!
34//! - **Target**: Windows only (`#[cfg(windows)]` should be used when
35//!   integrating)
36//! - **Dependencies**: Requires the `windows` crate for Windows API bindings
37//!
38//! ## Basic Usage
39//!
40//! ```rust,no_run,ignore
41//! use windows::Win32::Foundation::HWND;
42//! use windows::Win32::UI::WindowsAndMessaging::LoadIconW;
43//! use windows::Win32::UI::WindowsAndMessaging::WM_USER;
44//! use notify_icon::NotifyIcon;
45//!
46//! const IDI_APP_ICON: PCWSTR = PCWSTR(101 as *const u16);
47//!
48//! // Create and configure a notification icon
49//! let icon = unsafe { LoadIconW(hinstance, IDI_APP_ICON) }.unwrap_or_default();
50//! let icon = NotifyIcon::new()
51//!     .window_handle(hwnd)                    // Window to receive messages
52//!     .tip("My Application")                  // Tooltip text
53//!     .icon(icon_handle)                      // Icon to display
54//!     .callback_message(WM_USER + 1);        // Message ID for callbacks
55//!
56//! // Add the icon to the system tray
57//! icon.notify_add()?;
58//!
59//! // Later, remove the icon
60//! icon.notify_delete()?;
61//! ```
62//!
63//! ## Advanced Usage
64//!
65//! ### Using GUIDs for Icon Persistence
66//!
67//! ```rust,no_run,ignore
68//! use windows::core::GUID;
69//!
70//! let icon = NotifyIcon::new()
71//!     .window_handle(hwnd)
72//!     .guid(GUID::from_u128(0x12345678_1234_1234_1234_123456789ABC))
73//!     .tip("Persistent Icon")
74//!     .icon(icon_handle);
75//!
76//! icon.notify_add()?;
77//! ```
78//!
79//! ### Setting Interface Version for Enhanced Features
80//!
81//! ```rust,no_run,ignore
82//! use notify_icon::NotifyIcon;
83//!
84//! // Use Windows Vista+ behavior
85//! let icon = NotifyIcon::new()
86//!     .window_handle(hwnd)
87//!     .version(3)  // NOTIFYICON_VERSION_4
88//!     .tip("Modern Icon");
89//!
90//! icon.notify_add()?;
91//! icon.notify_set_version()?;  // Apply the version setting
92//! ```
93//!
94//! ### Modifying Existing Icons
95//!
96//! ```rust,no_run,ignore
97//! // Change the tooltip of an existing icon
98//! let updated_icon = icon.tip("Updated tooltip text");
99//! updated_icon.notify_modify()?;
100//! ```
101//!
102//! ## Message Handling
103//!
104//! When users interact with the notification icon, Windows sends messages to
105//! the specified window. Common message handling pattern:
106//!
107//! ```rust,no_run,ignore
108//! // In your window procedure
109//! match msg {
110//!     WM_USER + 1 => {  // Your callback message
111//!         match lparam {
112//!             WM_LBUTTONUP => {
113//!                 // Handle left click
114//!             },
115//!             WM_RBUTTONUP => {
116//!                 // Handle right click - typically show context menu
117//!             },
118//!             _ => {}
119//!         }
120//!     },
121//!     _ => {}
122//! }
123//! ```
124//!
125//! ## Error Handling
126//!
127//! All notification operations return [`windows::core::Result<()>`].
128//!
129//! ## Thread Safety
130//!
131//! The [`NotifyIcon`] struct is safe to use across threads.
132//!
133//! ## Limitations
134//!
135//! - **Windows only**: This library only works on Windows platforms
136
137use windows::{
138    Win32::{
139        Foundation::{FALSE, HWND},
140        UI::{
141            Shell::{
142                NIF_GUID, NIF_ICON, NIF_MESSAGE, NIF_SHOWTIP, NIF_TIP, NIM_ADD, NIM_DELETE,
143                NIM_MODIFY, NIM_SETFOCUS, NIM_SETVERSION, NOTIFY_ICON_DATA_FLAGS,
144                NOTIFY_ICON_MESSAGE, NOTIFYICONDATAW, Shell_NotifyIconW,
145            },
146            WindowsAndMessaging::HICON,
147        },
148    },
149    core::GUID,
150};
151
152/// A wrapper around the Windows NOTIFYICONDATAW structure for managing system
153/// tray icons in Windows.
154pub struct NotifyIcon {
155    /// Underlying internal data.
156    data: NOTIFYICONDATAW,
157}
158
159impl Default for NotifyIcon {
160    fn default() -> Self {
161        Self {
162            data: NOTIFYICONDATAW {
163                cbSize: std::mem::size_of::<NOTIFYICONDATAW>() as _,
164                ..Default::default()
165            },
166        }
167    }
168}
169
170impl NotifyIcon {
171    /// Creates a new [NotifyIcon] instance with default values.
172    ///
173    /// This is equivalent to calling [`NotifyIcon::default()`].
174    ///
175    /// # Returns
176    ///
177    /// A new [`NotifyIcon`] instance with the [`NOTIFYICONDATAW::cbSize`] field
178    /// properly initialized.
179    pub fn new() -> NotifyIcon {
180        Self::default()
181    }
182
183    /// Creates a new [NotifyIcon] instance with given `uID`.
184    ///
185    /// This is equivalent to calling [`NotifyIcon::default()`].
186    ///
187    /// # Returns
188    ///
189    /// A new [`NotifyIcon`] instance with the [`NOTIFYICONDATAW::cbSize`] field
190    /// properly initialized.
191    pub fn with_uid(uid: u32) -> NotifyIcon {
192        NotifyIcon {
193            data: NOTIFYICONDATAW {
194                uID: uid,
195                ..Default::default()
196            },
197        }
198    }
199
200    /// Sets a flag in the notification icon data structure.
201    ///
202    /// This method uses a bitwise OR operation to add the specified flag to the
203    /// existing flags in the [`NOTIFYICONDATAW::uFlags`].
204    ///
205    /// # Arguments
206    ///
207    /// * `flag` - A [`NOTIFY_ICON_DATA_FLAGS`] value to be added to the current
208    ///   flags
209    ///
210    /// # Returns
211    ///
212    /// Self for method chaining
213    pub fn flag(mut self, flag: NOTIFY_ICON_DATA_FLAGS) -> Self {
214        self.data.uFlags |= flag;
215        self
216    }
217
218    /// Sets the window handle that will receive notification messages.
219    ///
220    /// This method specifies the window that will receive callback messages
221    /// when the user interacts with the notification icon. The window
222    /// handle is required for the notification icon to function properly.
223    ///
224    /// # Arguments
225    ///
226    /// * `handle` - A handle ([HWND]) to the window that will receive
227    ///   notification messages
228    ///
229    /// # Returns
230    ///
231    /// Self for method chaining
232    pub fn window_handle(mut self, handle: HWND) -> Self {
233        self.data.hWnd = handle;
234        self.flag(NIF_MESSAGE)
235    }
236
237    /// Sets the tooltip text for the notification icon.
238    ///
239    /// The tooltip text is displayed when the user hovers over the icon in the
240    /// system tray. The text is converted to UTF-16 format and truncated if
241    /// it exceeds the maximum length. Automatically sets the [NIF_TIP] and
242    /// [NIF_SHOWTIP] flags.
243    ///
244    /// # Arguments
245    ///
246    /// * `s` - The tooltip text as any type that can be converted into a String
247    ///
248    /// # Returns
249    ///
250    /// Self for method chaining
251    pub fn tip(mut self, s: impl Into<String>) -> Self {
252        let s = s.into();
253        let tip_utf16 = s.encode_utf16().chain(Some(0)).collect::<Vec<u16>>();
254        let max_len = self.data.szTip.len() - 1;
255        if tip_utf16.len() <= max_len + 1 {
256            self.data.szTip[..tip_utf16.len()].copy_from_slice(&tip_utf16);
257        } else {
258            self.data.szTip[..max_len].copy_from_slice(&tip_utf16[..max_len]);
259            self.data.szTip[max_len] = 0;
260        }
261        self.flag(NIF_TIP | NIF_SHOWTIP)
262    }
263
264    /// Sets the icon for the notification area.
265    ///
266    /// This method assigns an icon handle to the notification icon and
267    /// automatically sets the [NIF_ICON] flag to indicate that the icon field
268    /// is valid.
269    ///
270    /// # Arguments
271    ///
272    /// * `icon` - An [HICON] handle to the icon to be displayed in the system
273    ///   tray
274    ///
275    /// # Returns
276    ///
277    /// Self for method chaining
278    pub fn icon(mut self, icon: HICON) -> Self {
279        self.data.hIcon = icon;
280        self.flag(NIF_ICON)
281    }
282
283    /// Sets the icon for balloon notifications.
284    ///
285    /// This icon is displayed in balloon tip notifications. The method
286    /// automatically sets the [NIF_ICON] flag to indicate that the balloon
287    /// icon field is valid.
288    ///
289    /// # Arguments
290    ///
291    /// * `icon` - An [HICON] handle to the icon to be displayed in balloon
292    ///   notifications
293    ///
294    /// # Returns
295    ///
296    /// Self for method chaining
297    pub fn balloon_icon(mut self, icon: HICON) -> Self {
298        self.data.hBalloonIcon = icon;
299        self.flag(NIF_ICON)
300    }
301
302    /// Sets the callback message identifier for the notification icon.
303    ///
304    /// When the user interacts with the notification icon (clicks,
305    /// double-clicks, etc.), Windows sends this message to the window
306    /// procedure. Automatically sets the [NIF_MESSAGE] flag.
307    ///
308    /// # Arguments
309    ///
310    /// * `callback_msg` - The message identifier that will be sent to the
311    ///   window procedure
312    ///
313    /// # Returns
314    ///
315    /// Self for method chaining
316    pub fn callback_message(mut self, callback_msg: u32) -> Self {
317        self.data.uCallbackMessage = callback_msg;
318        self.flag(NIF_MESSAGE)
319    }
320
321    /// Sets a GUID for the notification icon.
322    ///
323    /// The GUID provides a unique identifier for the notification icon, which
324    /// can be useful for maintaining icon state across application
325    /// restarts. Automatically sets the [NIF_GUID] flag.
326    ///
327    /// # Arguments
328    ///
329    /// * `guid` - A 128-bit unsigned integer representing the GUID
330    ///
331    /// # Returns
332    ///
333    /// Self for method chaining
334    pub fn guid(mut self, guid: impl Into<GUID>) -> Self {
335        self.data.guidItem = guid.into();
336        self.flag(NIF_GUID)
337    }
338
339    /// Sets the timeout duration for balloon tip notifications.
340    ///
341    /// This value specifies how long the balloon tip should be displayed before
342    /// automatically disappearing. The timeout is specified in milliseconds.
343    ///
344    /// **Note**: This field is deprecated as of Windows Vista. On Vista and
345    /// later, notification display times are based on system accessibility
346    /// settings. This field is only effective on Windows 2000 and Windows
347    /// XP.
348    ///
349    /// The system enforces minimum (10 seconds) and maximum (30 seconds)
350    /// timeout values.
351    ///
352    /// # Arguments
353    ///
354    /// * `timeout` - Timeout duration in milliseconds (only effective on
355    ///   Windows 2000/XP)
356    ///
357    /// # Returns
358    ///
359    /// Self for method chaining
360    pub fn timeout(mut self, timeout: u32) -> Self {
361        self.data.Anonymous.uTimeout = timeout;
362        self
363    }
364
365    /// Sets the version of the Shell notification icon interface to use.
366    ///
367    /// This method specifies which version of the notification icon interface
368    /// should be used, which affects the behavior of certain notification
369    /// features. The version determines whether to use Windows 95-style or
370    /// newer behavior for icon interactions.
371    ///
372    /// **Note**: This field shares the same memory location as `uTimeout` in a
373    /// union. This method should only be used when sending a
374    /// [`NIM_SETVERSION`] message via [`NotifyIcon::notify_set_version`]. For
375    /// balloon notifications, use [`NotifyIcon::timeout`] instead.
376    ///
377    /// Common version values:
378    /// - `0` (`NOTIFYICON_VERSION`): Use Windows 95-style behavior (default)
379    /// - `3` (`NOTIFYICON_VERSION_4`): Use Windows Vista and later behavior
380    /// - `4`: Use Windows 7 and later behavior
381    ///
382    /// # Arguments
383    ///
384    /// * `version` - The Shell notification icon interface version to use
385    ///
386    /// # Returns
387    ///
388    /// Self for method chaining
389    pub fn version(mut self, version: u32) -> Self {
390        self.data.Anonymous.uVersion = version;
391        self
392    }
393
394    /// Sends a notification message to the Windows shell.
395    ///
396    /// This is the core method that communicates with the Windows shell to
397    /// perform operations on the notification icon. It calls the
398    /// [Shell_NotifyIconW] function with the specified message and the
399    /// current icon data.
400    ///
401    /// # Arguments
402    ///
403    /// * `message` - The type of operation to perform (add, delete, modify,
404    ///   etc.)
405    ///
406    /// # Returns
407    ///
408    /// A [`windows::core::Result<()>`] indicating success or failure
409    ///
410    /// # Errors
411    ///
412    /// Returns an error if the Shell_NotifyIconW function fails
413    pub fn notify(&self, message: NOTIFY_ICON_MESSAGE) -> windows::core::Result<()> {
414        (unsafe { Shell_NotifyIconW(message, &self.data) } != FALSE)
415            .then_some(())
416            .ok_or_else(windows::core::Error::from_thread)
417    }
418
419    /// Adds the notification icon to the system tray.
420    ///
421    /// This method sends a [NIM_ADD] message to add the notification icon to
422    /// the notification area. The icon will appear in the system tray.
423    ///
424    /// # Returns
425    ///
426    /// A [`windows::core::Result<()>`] indicating success or failure
427    ///
428    /// # Errors
429    ///
430    /// Returns an error if the add operation fails
431    pub fn notify_add(&self) -> windows::core::Result<()> {
432        self.notify(NIM_ADD)
433    }
434
435    /// Removes the notification icon from the system tray.
436    ///
437    /// This method sends a [NIM_DELETE] message to remove the notification icon
438    /// from the notification area. The icon will disappear from the system
439    /// tray.
440    ///
441    /// # Returns
442    ///
443    /// A [`windows::core::Result<()>`] indicating success or failure
444    ///
445    /// # Errors
446    ///
447    /// Returns an error if the delete operation fails
448    pub fn notify_delete(&self) -> windows::core::Result<()> {
449        self.notify(NIM_DELETE)
450    }
451
452    /// Modifies an existing notification icon in the system tray.
453    ///
454    /// This method sends a [NIM_MODIFY] message to update the properties of an
455    /// existing notification icon. Only the fields that have their
456    /// corresponding flags set will be updated.
457    ///
458    /// # Returns
459    ///
460    /// A [`windows::core::Result<()>`] indicating success or failure
461    ///
462    /// # Errors
463    ///
464    /// Returns an error if the modify operation fails
465    pub fn notify_modify(&self) -> windows::core::Result<()> {
466        self.notify(NIM_MODIFY)
467    }
468
469    /// Sets focus to the notification icon.
470    ///
471    /// This method sends a [NIM_SETFOCUS] message to give focus to the
472    /// notification icon, which can be useful for accessibility purposes.
473    ///
474    /// # Returns
475    ///
476    /// A [`windows::core::Result<()>`] indicating success or failure
477    ///
478    /// # Errors
479    ///
480    /// Returns an error if the set focus operation fails
481    pub fn notify_set_focus(&self) -> windows::core::Result<()> {
482        self.notify(NIM_SETFOCUS)
483    }
484
485    /// Sets the version of the notification icon interface.
486    ///
487    /// This method sends a [NIM_SETVERSION] message to specify which version of
488    /// the notification icon interface to use. This affects the behavior of
489    /// certain notification features.
490    ///
491    /// # Returns
492    ///
493    /// A [`windows::core::Result<()>`] indicating success or failure
494    ///
495    /// # Errors
496    ///
497    /// Returns an error if the set version operation fails
498    pub fn notify_set_version(&self) -> windows::core::Result<()> {
499        self.notify(NIM_SETVERSION)
500    }
501}