#notifications #gntp #growl

bin+lib sendgrowl

Send notifications to Growl with multiple host and port support

1 unstable release

Uses new Rust 2024

0.7.4 Dec 20, 2025

#238 in #notifications

MIT license

48KB
616 lines

sendgrowl

Send notifications to Growl with multiple host and port support (rust)

🚀 installation

cargo build --release # then use `sendgrowl -h`

or

cargo install sendgrowl

👤 Author

Hadi Cahyadi

Buy Me a Coffee

Donate via Ko-fi

Support me on Patreon


lib.rs:

GNTP - Growl Notification Transport Protocol Client

A robust, production-ready Rust implementation of the Growl Notification Transport Protocol (GNTP) for sending desktop notifications to Growl-compatible clients across multiple platforms.

Features

  • Full GNTP 1.0 protocol implementation
  • Multiple icon delivery modes (Binary, File URL, Data URL/Base64)
  • Windows Growl compatibility with automatic workarounds
  • Cross-platform support (Windows, macOS, Linux)
  • Binary resource deduplication to prevent protocol errors
  • Comprehensive error handling with detailed error types
  • Production-ready with extensive testing
  • No external dependencies (except uuid for unique identifiers)

Platform Compatibility

Platform Binary Mode File URL Data URL Recommended
Windows (Growl for Windows) ⚠️ Buggy ✅ Works Best DataUrl
macOS (Growl) ✅ Works ✅ Works ✅ Works Binary
Linux (Growl-compatible) ✅ Works ✅ Works ✅ Works Binary

Quick Start

use gntp::{GntpClient, NotificationType, Resource, IconMode};

// Create client with DataUrl mode (safest, most compatible)
let mut client = GntpClient::new("My App")
    .with_icon_mode(IconMode::DataUrl);

// Load icon from file
let icon = Resource::from_file("icon.png")?;

// Define notification type with icon
let notification = NotificationType::new("alert")
    .with_display_name("Alert Notification")
    .with_icon(icon);

// Register (must be called first!)
client.register(vec![notification])?;

// Send notification
client.notify("alert", "Hello", "This is a test notification")?;

Icon Delivery Modes

Binary Mode (GNTP Spec Compliant)

let client = GntpClient::new("App")
    .with_icon_mode(IconMode::Binary);

Sends icons as binary resources according to GNTP specification. Note: May not work correctly with Growl for Windows due to implementation bugs.

File URL Mode

let client = GntpClient::new("App")
    .with_icon_mode(IconMode::FileUrl);

References icons via file:// URLs. Requires icon files to exist on disk.

let client = GntpClient::new("App")
    .with_icon_mode(IconMode::DataUrl); // Default

Embeds icons as base64-encoded data URLs. Most compatible across all platforms. No external files required.

Advanced Usage

Creating Resources from Memory

let image_data: Vec<u8> = vec![/* your image bytes */];
let icon = Resource::from_bytes(image_data, "image/png");

Sending Notifications with Options

let options = NotifyOptions::new()
    .with_sticky(true)
    .with_priority(2)
    .with_icon(icon);

client.notify_with_options(
    "alert",
    "Important",
    "This stays on screen",
    options
)?;

Multiple Notification Types

let info = NotificationType::new("info")
    .with_display_name("Information");
let warning = NotificationType::new("warning")
    .with_display_name("Warning");
let error = NotificationType::new("error")
    .with_display_name("Error");

client.register(vec![info, warning, error])?;

client.notify("info", "Info", "Something happened")?;
client.notify("warning", "Warning", "Be careful!")?;
client.notify("error", "Error", "Something went wrong!")?;

Remote Notifications

let client = GntpClient::new("Remote App")
    .with_host("192.168.1.100")
    .with_port(23053);

Debug Mode

let client = GntpClient::new("Debug App")
    .with_debug(true); // Prints detailed packet information

Protocol Specification

GNTP requires two separate steps:

  1. REGISTER - Register your application and notification types (once per connection)
  2. NOTIFY - Send notifications (multiple times)

You must call register() before calling notify(), otherwise you'll receive a ProtocolError.

Error Handling

All operations return Result<T, GntpError> with detailed error information:

match client.register(vec![]) {
    Ok(_) => println!("Registered successfully"),
    Err(GntpError::ConnectionError(msg)) => {
        eprintln!("Connection failed: {}", msg);
    }
    Err(GntpError::IoError(msg)) => {
        eprintln!("I/O error: {}", msg);
    }
    Err(GntpError::ProtocolError(msg)) => {
        eprintln!("Protocol error: {}", msg);
    }
}

Windows Compatibility Notes

Growl for Windows has a known bug where it doesn't properly handle binary resources according to the GNTP specification. When the server receives binary data, it may not respond, causing timeout errors (10060).

Solution: Use IconMode::DataUrl (default) which embeds icons as base64 strings. This bypasses the binary resource issue entirely.

Performance Considerations

  • Binary Mode: Smallest packet size, fastest transmission
  • File URL Mode: No data in packet, but requires disk access
  • Data URL Mode: Larger packets (~33% increase due to base64), but most reliable

For typical notification icons (< 100KB), the performance difference is negligible.

Examples

See the examples directory for:

  • Basic notifications
  • Notifications with icons
  • Multiple notification types
  • Remote notifications
  • Error handling patterns

License

MIT License - See LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Credits

Author: Hadi Cahyadi cumulus13@gmail.com

Dependencies

~2–12MB
~85K SLoC