Perl scripts often live in the terminal or deep inside a server handing out webpages or whatnot, but they don't have to stay there. Earlier, in chapter 2, we demonstrated using system libraries on Windows to display a simple MessageBox. This time, we turn to Linux (and BSDs with a desktop environment).
The libnotify library allows applications to send pop-up notifications to the user. These are the bubbles you see from apps like Slack, Discord, or your system updater.
This library uses the GLib object system (GObject), which is notoriously complex to bind manually. However, if we only want to trigger a notification, we can treat the objects as opaque pointers and ignore the internal complexity entirely.
The Recipe
use v5.40;
use Affix;
# 1. Find the library
# locate_lib searches system paths (like /usr/lib) for libnotify.so
# It returns the full path as a string.
my $libnotify = Affix::locate_lib('notify');
unless ($libnotify) {
die "Could not find libnotify. Are you on Linux with libnotify-bin installed?";
}
# 2. Bind the lifecycle functions
# bool notify_init(const char *app_name);
affix $libnotify, 'notify_init', [String] => Bool;
affix $libnotify, 'notify_uninit', [] => Void;
# 3. Bind the object functions
# NotifyNotification* notify_notification_new(const char *summary, const char *body, const char *icon);
affix $libnotify, 'notify_notification_new', [ String, String, String ] => Pointer [Void];
# bool notify_notification_show(NotifyNotification *notification, GError **error);
affix $libnotify, 'notify_notification_show', [ Pointer [Void], Pointer [Void] ] => Bool;
# 4. Use it
# Initialize the library with our app name
notify_init('Affix');
# Create the notification object
# We get back a Pin (opaque pointer), but we don't need to look inside it.
my $n = notify_notification_new(
'Keep your stick on the ice! 🏒', #
"Hello from Affix!\nWelcome to the fun world of FFI.", #
'dialog-information' # Standard icon name
);
# Show it
# We pass undef for the second argument (GError**), meaning "ignore errors".
notify_notification_show( $n, undef );
# Clean up
notify_uninit();
How It Works
-
1. Locating libraries
my $libnotify = Affix::locate_lib('notify');Unlike
load_library(which loads the lib into memory immediately),locate_libscans the system's dynamic linker paths (likeLD_LIBRARY_PATH) and simply returns the filename. We pass this path toaffix, which handles the loading. -
2. Ignoring complexity
The
notify_notification_newfunction returns aNotifyNotification*. In C, this is a struct with many private fields and GObject metadata. In Perl, we don't care. We define the return type asPointer[Void].As long as we pass that pointer back to other functions in the same library (like
notify_notification_show), everything works. -
3. Optional Error Handling
The C signature for
showis:gboolean notify_notification_show (NotifyNotification *notification, GError **error);The second argument is a pointer-to-a-pointer that the library fills if an error occurs. By passing
undef, Affix sends aNULL, telling the library we don't want to receive error details.
Kitchen Reminders
-
Platform specific
This recipe works on Linux/BSD with a freedesktop.org compliant notification daemon (GNOME, KDE, XFCE, etc.). It will fail to find the library on Windows or vanilla macOS.
-
Unicode
Notice we used an emoji in the notification. Because we used the
Stringtype, Affix automatically encoded this as UTF-8, which is what modern Linux libraries expect.