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 /// Sets a flag in the notification icon data structure.
184 ///
185 /// This method uses a bitwise OR operation to add the specified flag to the
186 /// existing flags in the [`NOTIFYICONDATAW::uFlags`].
187 ///
188 /// # Arguments
189 ///
190 /// * `flag` - A [`NOTIFY_ICON_DATA_FLAGS`] value to be added to the current
191 /// flags
192 ///
193 /// # Returns
194 ///
195 /// Self for method chaining
196 pub fn flag(mut self, flag: NOTIFY_ICON_DATA_FLAGS) -> Self {
197 self.data.uFlags |= flag;
198 self
199 }
200
201 /// Sets the window handle that will receive notification messages.
202 ///
203 /// This method specifies the window that will receive callback messages
204 /// when the user interacts with the notification icon. The window
205 /// handle is required for the notification icon to function properly.
206 ///
207 /// # Arguments
208 ///
209 /// * `handle` - A handle ([HWND]) to the window that will receive
210 /// notification messages
211 ///
212 /// # Returns
213 ///
214 /// Self for method chaining
215 pub fn window_handle(mut self, handle: HWND) -> Self {
216 self.data.hWnd = handle;
217 self.flag(NIF_MESSAGE)
218 }
219
220 /// Sets the tooltip text for the notification icon.
221 ///
222 /// The tooltip text is displayed when the user hovers over the icon in the
223 /// system tray. The text is converted to UTF-16 format and truncated if
224 /// it exceeds the maximum length. Automatically sets the [NIF_TIP] and
225 /// [NIF_SHOWTIP] flags.
226 ///
227 /// # Arguments
228 ///
229 /// * `s` - The tooltip text as any type that can be converted into a String
230 ///
231 /// # Returns
232 ///
233 /// Self for method chaining
234 pub fn tip(mut self, s: impl Into<String>) -> Self {
235 let s = s.into();
236 let tip_utf16 = s.encode_utf16().chain(Some(0)).collect::<Vec<u16>>();
237 let max_len = self.data.szTip.len() - 1;
238 if tip_utf16.len() <= max_len + 1 {
239 self.data.szTip[..tip_utf16.len()].copy_from_slice(&tip_utf16);
240 } else {
241 self.data.szTip[..max_len].copy_from_slice(&tip_utf16[..max_len]);
242 self.data.szTip[max_len] = 0;
243 }
244 self.flag(NIF_TIP | NIF_SHOWTIP)
245 }
246
247 /// Sets the icon for the notification area.
248 ///
249 /// This method assigns an icon handle to the notification icon and
250 /// automatically sets the [NIF_ICON] flag to indicate that the icon field
251 /// is valid.
252 ///
253 /// # Arguments
254 ///
255 /// * `icon` - An [HICON] handle to the icon to be displayed in the system
256 /// tray
257 ///
258 /// # Returns
259 ///
260 /// Self for method chaining
261 pub fn icon(mut self, icon: HICON) -> Self {
262 self.data.hIcon = icon;
263 self.flag(NIF_ICON)
264 }
265
266 /// Sets the icon for balloon notifications.
267 ///
268 /// This icon is displayed in balloon tip notifications. The method
269 /// automatically sets the [NIF_ICON] flag to indicate that the balloon
270 /// icon field is valid.
271 ///
272 /// # Arguments
273 ///
274 /// * `icon` - An [HICON] handle to the icon to be displayed in balloon
275 /// notifications
276 ///
277 /// # Returns
278 ///
279 /// Self for method chaining
280 pub fn balloon_icon(mut self, icon: HICON) -> Self {
281 self.data.hBalloonIcon = icon;
282 self.flag(NIF_ICON)
283 }
284
285 /// Sets the callback message identifier for the notification icon.
286 ///
287 /// When the user interacts with the notification icon (clicks,
288 /// double-clicks, etc.), Windows sends this message to the window
289 /// procedure. Automatically sets the [NIF_MESSAGE] flag.
290 ///
291 /// # Arguments
292 ///
293 /// * `callback_msg` - The message identifier that will be sent to the
294 /// window procedure
295 ///
296 /// # Returns
297 ///
298 /// Self for method chaining
299 pub fn callback_message(mut self, callback_msg: u32) -> Self {
300 self.data.uCallbackMessage = callback_msg;
301 self.flag(NIF_MESSAGE)
302 }
303
304 /// Sets a GUID for the notification icon.
305 ///
306 /// The GUID provides a unique identifier for the notification icon, which
307 /// can be useful for maintaining icon state across application
308 /// restarts. Automatically sets the [NIF_GUID] flag.
309 ///
310 /// # Arguments
311 ///
312 /// * `guid` - A 128-bit unsigned integer representing the GUID
313 ///
314 /// # Returns
315 ///
316 /// Self for method chaining
317 pub fn guid(mut self, guid: impl Into<GUID>) -> Self {
318 self.data.guidItem = guid.into();
319 self.flag(NIF_GUID)
320 }
321
322 /// Sets the timeout duration for balloon tip notifications.
323 ///
324 /// This value specifies how long the balloon tip should be displayed before
325 /// automatically disappearing. The timeout is specified in milliseconds.
326 ///
327 /// **Note**: This field is deprecated as of Windows Vista. On Vista and
328 /// later, notification display times are based on system accessibility
329 /// settings. This field is only effective on Windows 2000 and Windows
330 /// XP.
331 ///
332 /// The system enforces minimum (10 seconds) and maximum (30 seconds)
333 /// timeout values.
334 ///
335 /// # Arguments
336 ///
337 /// * `timeout` - Timeout duration in milliseconds (only effective on
338 /// Windows 2000/XP)
339 ///
340 /// # Returns
341 ///
342 /// Self for method chaining
343 pub fn timeout(mut self, timeout: u32) -> Self {
344 self.data.Anonymous.uTimeout = timeout;
345 self
346 }
347
348 /// Sets the version of the Shell notification icon interface to use.
349 ///
350 /// This method specifies which version of the notification icon interface
351 /// should be used, which affects the behavior of certain notification
352 /// features. The version determines whether to use Windows 95-style or
353 /// newer behavior for icon interactions.
354 ///
355 /// **Note**: This field shares the same memory location as `uTimeout` in a
356 /// union. This method should only be used when sending a
357 /// [`NIM_SETVERSION`] message via [`NotifyIcon::notify_set_version`]. For
358 /// balloon notifications, use [`NotifyIcon::timeout`] instead.
359 ///
360 /// Common version values:
361 /// - `0` (`NOTIFYICON_VERSION`): Use Windows 95-style behavior (default)
362 /// - `3` (`NOTIFYICON_VERSION_4`): Use Windows Vista and later behavior
363 /// - `4`: Use Windows 7 and later behavior
364 ///
365 /// # Arguments
366 ///
367 /// * `version` - The Shell notification icon interface version to use
368 ///
369 /// # Returns
370 ///
371 /// Self for method chaining
372 pub fn version(mut self, version: u32) -> Self {
373 self.data.Anonymous.uVersion = version;
374 self
375 }
376
377 /// Sends a notification message to the Windows shell.
378 ///
379 /// This is the core method that communicates with the Windows shell to
380 /// perform operations on the notification icon. It calls the
381 /// [Shell_NotifyIconW] function with the specified message and the
382 /// current icon data.
383 ///
384 /// # Arguments
385 ///
386 /// * `message` - The type of operation to perform (add, delete, modify,
387 /// etc.)
388 ///
389 /// # Returns
390 ///
391 /// A [`windows::core::Result<()>`] indicating success or failure
392 ///
393 /// # Errors
394 ///
395 /// Returns an error if the Shell_NotifyIconW function fails
396 pub fn notify(&self, message: NOTIFY_ICON_MESSAGE) -> windows::core::Result<()> {
397 (unsafe { Shell_NotifyIconW(message, &self.data) } != FALSE)
398 .then_some(())
399 .ok_or_else(windows::core::Error::from_win32)
400 }
401
402 /// Adds the notification icon to the system tray.
403 ///
404 /// This method sends a [NIM_ADD] message to add the notification icon to
405 /// the notification area. The icon will appear in the system tray.
406 ///
407 /// # Returns
408 ///
409 /// A [`windows::core::Result<()>`] indicating success or failure
410 ///
411 /// # Errors
412 ///
413 /// Returns an error if the add operation fails
414 pub fn notify_add(&self) -> windows::core::Result<()> {
415 self.notify(NIM_ADD)
416 }
417
418 /// Removes the notification icon from the system tray.
419 ///
420 /// This method sends a [NIM_DELETE] message to remove the notification icon
421 /// from the notification area. The icon will disappear from the system
422 /// tray.
423 ///
424 /// # Returns
425 ///
426 /// A [`windows::core::Result<()>`] indicating success or failure
427 ///
428 /// # Errors
429 ///
430 /// Returns an error if the delete operation fails
431 pub fn notify_delete(&self) -> windows::core::Result<()> {
432 self.notify(NIM_DELETE)
433 }
434
435 /// Modifies an existing notification icon in the system tray.
436 ///
437 /// This method sends a [NIM_MODIFY] message to update the properties of an
438 /// existing notification icon. Only the fields that have their
439 /// corresponding flags set will be updated.
440 ///
441 /// # Returns
442 ///
443 /// A [`windows::core::Result<()>`] indicating success or failure
444 ///
445 /// # Errors
446 ///
447 /// Returns an error if the modify operation fails
448 pub fn notify_modify(&self) -> windows::core::Result<()> {
449 self.notify(NIM_MODIFY)
450 }
451
452 /// Sets focus to the notification icon.
453 ///
454 /// This method sends a [NIM_SETFOCUS] message to give focus to the
455 /// notification icon, which can be useful for accessibility purposes.
456 ///
457 /// # Returns
458 ///
459 /// A [`windows::core::Result<()>`] indicating success or failure
460 ///
461 /// # Errors
462 ///
463 /// Returns an error if the set focus operation fails
464 pub fn notify_set_focus(&self) -> windows::core::Result<()> {
465 self.notify(NIM_SETFOCUS)
466 }
467
468 /// Sets the version of the notification icon interface.
469 ///
470 /// This method sends a [NIM_SETVERSION] message to specify which version of
471 /// the notification icon interface to use. This affects the behavior of
472 /// certain notification features.
473 ///
474 /// # Returns
475 ///
476 /// A [`windows::core::Result<()>`] indicating success or failure
477 ///
478 /// # Errors
479 ///
480 /// Returns an error if the set version operation fails
481 pub fn notify_set_version(&self) -> windows::core::Result<()> {
482 self.notify(NIM_SETVERSION)
483 }
484}