| Crates.io | gntp |
| lib.rs | gntp |
| version | 0.1.11 |
| created_at | 2025-12-01 09:00:04.539867+00 |
| updated_at | 2025-12-20 08:26:07.907006+00 |
| description | Production-ready GNTP (Growl Notification Transport Protocol) client with Windows/Android compatibility and multiple icon delivery modes. Includes sendgrowl CLI tool. |
| homepage | https://github.com/cumulus13/gntp |
| repository | https://github.com/cumulus13/gntp |
| max_upload_size | |
| id | 1959543 |
| size | 134,228 |
A robust, production-ready Rust implementation of the Growl Notification Transport Protocol (GNTP) for sending desktop notifications to Growl-compatible clients across multiple platforms.
uuid for unique identifiers)Add to your Cargo.toml:
[dependencies]
gntp = "0.1.5"
You need a GNTP-compatible notification client:
Default server: localhost:23053
# Build library only (no CLI) `sendgrowl`
cargo build --release
# Build dengan CLI tool (sendgrowl)
cargo build --release --features cli --bin sendgrowl
# Test sendgrowl
cargo run --features cli --bin sendgrowl -- TEST APP "Title" "Message" -i growl.png -v
# more options
sendgrowl --help
use gntp::{GntpClient, NotificationType, Resource, IconMode};
fn main() -> Result<(), gntp::GntpError> {
// 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")?;
Ok(())
}
Embeds icons as base64-encoded data URLs. Most compatible across all platforms, especially Windows.
let client = GntpClient::new("App")
.with_icon_mode(IconMode::DataUrl);
Pros:
Cons:
Sends icons as binary resources according to GNTP specification.
let client = GntpClient::new("App")
.with_icon_mode(IconMode::Binary);
Pros:
Cons:
References icons via file:// URLs. Requires icon files to exist on disk.
let client = GntpClient::new("App")
.with_icon_mode(IconMode::FileUrl);
Pros:
Cons:
let icon = Resource::from_file("icon.png")?;
let image_data: Vec<u8> = load_icon_from_memory();
let icon = Resource::from_bytes(image_data, "image/png");
.png) - Recommended.jpg, .jpeg).gif).bmp).ico).svg).webp)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!")?;
use gntp::NotifyOptions;
let options = NotifyOptions::new()
.with_sticky(true) // Stays on screen until dismissed
.with_priority(2); // Emergency priority
client.notify_with_options(
"alert",
"Important",
"This stays on screen",
options
)?;
let client = GntpClient::new("Remote App")
.with_host("192.168.1.100")
.with_port(23053);
Android devices may have network delays. Use retry mechanism:
# sendgrowl with retry
sendgrowl.exe MyApp Event "Title" "Message" \
-H 192.168.1.50 \
-r 3 \ # Retry 3 times on failure
--retry-delay 2000 # Wait 2 seconds between retries
Or in Rust code with manual retry:
let mut client = GntpClient::new("Android App")
.with_host("192.168.1.50")
.with_icon_mode(IconMode::DataUrl);
for attempt in 1..=3 {
match client.register(vec![notification.clone()]) {
Ok(_) => break,
Err(e) if attempt < 3 => {
eprintln!("Retry {}/3...", attempt);
std::thread::sleep(Duration::from_secs(2));
}
Err(e) => return Err(e),
}
}
let client = GntpClient::new("Debug App")
.with_debug(true); // Prints detailed packet information
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 (error code 10060).
Solution: Use IconMode::DataUrl (default) which embeds icons as base64 strings. This bypasses the binary resource issue entirely.
| 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 |
Buggy: most tests pass
match client.register(vec![notification]) {
Ok(_) => println!("Registered successfully"),
Err(gntp::GntpError::ConnectionError(msg)) => {
eprintln!("Connection failed: {}", msg);
}
Err(gntp::GntpError::IoError(msg)) => {
eprintln!("I/O error: {}", msg);
}
Err(gntp::GntpError::ProtocolError(msg)) => {
eprintln!("Protocol error: {}", msg);
}
}
Run examples with:
# Basic notification
cargo run --example basic
# Notification with icon
cargo run --example with_icon
# Notification with full path icon
cargo run --example with_icon_binary
# Multiple notification types
cargo run --example multiple_types
# Remote notifications
GROWL_HOST=192.168.1.100 cargo run --example remote
# Android notifications with retry
ANDROID_HOST=192.168.1.50 cargo run --example android
# Error handling patterns
cargo run --example error_handling
use gntp::{GntpClient, NotificationType};
fn main() {
let mut client = GntpClient::new("Example App");
let notification = NotificationType::new("message");
client.register(vec![notification]).unwrap();
client.notify("message", "Hello", "Basic notification").unwrap();
}
use gntp::{GntpClient, NotificationType, Resource};
let mut client = GntpClient::new("Icon Example");
// Load application icon
if let Ok(icon) = Resource::from_file("app_icon.png") {
client = client.with_icon(icon);
}
let notification = NotificationType::new("alert");
client.register(vec![notification])?;
client.notify("alert", "Alert", "With icon")?;
use gntp::{GntpClient, NotificationType, NotifyOptions};
let mut client = GntpClient::new("Options Example");
let notification = NotificationType::new("important");
client.register(vec![notification])?;
let options = NotifyOptions::new()
.with_sticky(true)
.with_priority(2);
client.notify_with_options(
"important",
"Critical",
"High priority sticky notification",
options
)?;
let mut client = GntpClient::new("Multi App");
let notifications = vec![
NotificationType::new("info"),
NotificationType::new("warning"),
NotificationType::new("error"),
];
client.register(notifications)?;
client.notify("info", "Info", "Information")?;
client.notify("warning", "Warning", "Warning message")?;
client.notify("error", "Error", "Error occurred")?;
GNTP requires two separate steps:
Icons are sent as binary resources with unique identifiers, not as file paths.
match client.register(vec![notification]) {
Ok(_) => println!("Registered successfully"),
Err(e) => {
eprintln!("Registration failed: {}", e);
// Handle error (Growl not running, network issue, etc.)
}
}
# Basic example
cargo run --example basic
# With icon
cargo run --example with_icon
# Multiple notification types
cargo run --example multiple_types
# With options (priority, sticky)
cargo run --example with_options
# Error handling
cargo run --example error_handling
Licensed under either of:
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)Note: This is a production-ready library with comprehensive Windows compatibility. If you encounter any issues, please open an issue on GitHub.