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}