Hack and /

Message for You Sir

Kyle Rankin

Issue #188, December 2009

Why check for new e-mail every minute when a script can do it for you? Learn how to trigger desktop notifications in your own scripts.

It's easy to forget dæmons are there unless they demand your attention. A few years ago, I was walking through the expo floor at OSCON, when I noticed someone in a full BSD dæmon costume getting his picture taken with a few fans. When I saw them trying to figure out how to arrange everyone for the picture I couldn't help but yell, “No! The dæmon is always in the background!”

In case you didn't get the joke, dæmon is a name UNIX people give to processes that run behind the scenes (in the background). Dæmons perform all sorts of useful functions from executing scripts at a certain time (atd and crond) to listening for network connections and spawning the appropriate process to serve the request (inetd). In fact, the d at the end of those scripts stands for dæmon, and you might notice that a number of processes on your system right now end in d.

The whole point of a dæmon is to perform tasks without your intervention or knowledge, but sometimes, it's handy for a dæmon to alert you when a certain condition occurs. On a server, this usually means the dæmon will send an e-mail alert to the administrator, but what about on a desktop? What if you want a dæmon to alert you when you have new e-mail? In that case, it makes more sense for some sort of notice to pop up on your desktop. In this column, I discuss three different methods I use so that dæmons can get my attention on my desktop.

A Case of OSD

I think the first time I noticed OSD (On-Screen Display) notifications on Linux was with a volume control program. I increased the volume on my computer, and right in the middle of the screen was a volume meter floating above all my other windows, just like on a TV. I instantly was intrigued and had to figure out how they did it. These days, there are a number of different OSD libraries and programs, but my favorite is still osd_cat.

The osd_cat program is a command-line program that displays text sent to it in a pipe. The fact that it accepts piped input makes it ideal for dæmon notification, because it's easy to add to any shell script. This command is part of the xosd-bin package on Debian-based systems, or xosd on Red Hat, and has been around for a number of years.

The simplest way to test osd_cat is to pipe some text to it:

$ echo "Hello World" | osd_cat

If you look at the top left-hand side of your screen, you should see your message appear in a small red font for a few seconds and then disappear. Of course, if you didn't know to look there, you might assume the program is broken because the message is so small. Plus, everyone knows green is the ideal foreground color, so let's spruce up that notification a bit and put it front and center:

$ echo "Hello World" | osd_cat --align=center --pos=bottom 
 ↪--color=green --font=lucidasanstypewriter-bold-24

Ahh, that's more like it, a notification right in the middle of the screen. As you can see, osd_cat accepts a number of options that can control how and where it displays your message. The man page covers all the options in detail, but I highlight the options I used here. The --align argument controls the text alignment much like a word processor and can be set to left (default), center or right. The --pos option controls the Y orientation on the screen and can be set to top (default), middle or bottom. The --color option is self-explanatory, as is --font. If you do want a different font but aren't sure what value to use, just run xlsfonts to see a full list.

In addition to the options I listed, osd_cat has many other options, such as --indent and --offset, that allow you to fine-tune where it displays your text, so you can position it virtually anywhere on the screen. You also can tweak the time the message appears on the screen with the --delay option, and if you plan to pipe multiple lines of text to the screen, be sure to look into the --lines, --age and --wait options, so you can control how osd_cat will scroll multiple lines. There are even --barmode and --percentage options that let you draw a slider bar, much like the OSD volume control I saw.

All of those options are nice, but honestly, I find myself sticking to basic text notifications with osd_cat, and although I have migrated many of my scripts to libnotify, I still like to use osd_cat for audio/video notification, such as when I use a script I wrote to turn on the VGA output on my laptop for presentations. I mentioned this script in my original Lightning Hacks column, but in case you didn't see it, here it is again:

#!/bin/sh

if xrandr | grep -q 'VGA connected'; then
    echo "LVDS + VGA" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
# choose my laptop screen's resolution by default; 
# if that fails, try the auto-detected mode
    xrandr --output VGA --mode 1280x768@60 || xrandr 
 ↪--output VGA --auto
else
    echo "LVDS only" | osd_cat --shadow=2 --align=center 
 ↪--pos=bottom --color=green --delay=2 
 ↪--font=lucidasanstypewriter-bold-24 --offset 40 &
    xrandr --output VGA --off &
fi

I find it's nice to add visual notifications like this whenever I trigger a script in the background with a keybinding and it's not immediately obvious the script ran, such as in this case, when I'd like to know whether I'm in presentation or regular mode, and it might take the projector a few seconds to respond.

Consider Yourself Notified

I used to use OSD notifications for all sorts of scripts on my system, including one that notified me when I got new e-mail. It worked fine, but sometimes I'd rather get a notification that's a little easier to ignore. Although I suppose I could move my OSD alert to a corner of the screen, then it would be almost too easy to miss. I wanted something that would get my attention but also would not get in the way. Currently, I use GNOME as my desktop environment, and I realized that its desktop notifications were ideal. They caught the corner of my eye, but they didn't jump in front of everything else I'm doing.

The library GNOME uses for notifications is the appropriately named libnotify, and it turns out, it was trivial to migrate my notifications from osd_cat to libnotify using the notify-send command. Because I already was using GNOME, the program already was installed. If it's not in your case, look for a package named libnotify-bin or search your package manager for notify-send.

The syntax for notify-send is substantially simpler than osd_cat, as the message's location and font already are handled for you. Here's a simple example:

$ notify-send "Message for you Sir" "Hello World"

This will pop up a basic message in a notification window on my desktop. The first set of quotes specifies the title of the message, and the next set of quotes defines the body of the message.

Of course, when I use notify-send to alert me of new e-mail, I use something a bit fancier. If you'd like to set up e-mail notification for yourself, here's a more simplified version of my personal script to get you started. First, set up fetchmail so that it can connect to your IMAP server. Just as a warning, don't ever run fetchmail without the -c option, unless you do want to download all your mail to your local machine. Once fetchmail is configured, you can test that it works with fetchmail -c:

$ fetchmail -c
991 messages (990 seen) for kyle at mail.example.net 
 (folder INBOX).
530 messages (530 seen) for kyle at mail.example.net 
 (folder INBOX.nblug).
284 messages (284 seen) for kyle at mail.example.net 
 (folder INBOX.linuxjournal).

As you can see, there's a new message that I haven't seen in my INBOX folder. All you need to do now is write a script that will execute fetchmail -c, parse the output, and tally the total and seen messages. If the totals differ, you have new mail and can execute notify-send with the appropriate message. Here's a quick Perl script that goes a step further and keeps track of each folder with new messages as well, so it can list them and their tally:

#!/usr/bin/perl

open FETCHMAIL, "/usr/bin/fetchmail -t 10 -c 2>/dev/null 
 ↪|" or die "Can't run fetchmail: $!\n";

while(<FETCHMAIL>){
   if(/^(\d+) messages \((\d+) seen.*?folder (.*?)\)/){
   # keep a running total of all messages and seen messages
      $messages+=$1; $seen+=$2; 
      $folder=$3;
      $folder =~ s/INBOX\.//; # strip the INBOX. 
                              # from the folder names
   }
# If there are more messages than seen messages, 
# store the difference
   if($1 > $2){
      $folders{$folder} = $1 - $2;
   }
}
close FETCHMAIL;

$total = $messages - $seen;

if($total > 0){
   foreach $folder (sort { $folders{$a}<=>$folders{$b} 
   ↪} keys %folders){
      push @list, "$folder:$folders{$folder}";
   }
   $output = join " ", @list;
   system ("notify-send -u low -i /usr/share/pixmaps/mutt.xpm 
   ↪-t 5000 'New Mail' '$output'");
}

Notice I added a few extra options to my notify-send in this example. First, I used the -u option so I could set the urgency of the message to either low, normal or critical. The -i option lets me specify an icon to add to the image, so I picked my system's mutt icon, because that's the program I'll use to read the mail. Next, I used the -t option so I could set the timeout for the message in milliseconds. Finally, I added the title and body of my message.

If you set this up yourself, all you have to do now is save the script and add it to your user's crontab so it will run however often you want to check for new mail. I also recommend adding some sort of throttling to the script, so it will notify you of a current batch of new mail only a few times. That way if you can't get to your e-mail immediately, the notifications won't become annoying.

Don't Forget to Blink

Desktop notifications are great, but what happens if I'm not looking at the screen when a notification appears? Sure, if the script runs every minute, I eventually will see it, but I set up throttling for those sorts of notifications. I came up with a notification that's even less intrusive than libnotify, yet it will alert me of new mail even if my screen idles out and goes blank: my keyboard LEDs.

Now there certainly is nothing new about using keyboard LEDs for notifications—after all, their intended purpose is to notify you about the state of your caps lock, num lock and scroll lock keys. But, how often do you use your scroll lock or even your caps lock these days? I mean, a number of my friends even went full-UNIX nerd on me and remapped caps lock back to the Ctrl key. Your keyboard LEDs are three notification areas begging to be used, and Linux has plenty of utilities that can use them.

I've tried a few different tools that let me control the keyboard LEDs from a script, but I've settled on blinkd as my favorite. This program runs as a dæmon (see the d at the end?) when the system starts up, and what I like about it is that it not only lets you control all three keyboard LEDs, but you also can set a number of times for it to blink the LED—perfect for keeping track of new e-mail messages.

To install blinkd, look for the package of the same name with your package manager. Once it's installed, if your package manager didn't automatically start it, run /etc/init.d/blinkd start. After the dæmon is running, you can control the LEDs via the blink command. The syntax is pretty simple. For instance, if I want to blink the scroll lock key a single time, I would type:

$ blink -s -r 1

The -s argument tells it to activate the scroll lock LED, and -r tells it how many times to blink before it pauses. I also could have used -c or -n instead of -s to blink the caps lock or num lock LEDs, respectively. You also can set the number of blinks to 0 to turn off blinking for a specific LED, or type blink -r 0 to turn off blinking for all of the LEDs.

Because the script I wrote above already has the total number of new messages, it's trivial to have my scroll lock key blink that number of times. Here's the modified section of my script:

if($total > 0){
   foreach $folder (sort { $folders{$a}<=>$folders{$b} 
   ↪} keys %folders){
      push @list, "$folder:$folders{$folder}";
   }
   $output = join " ", @list;
   system ("notify-send -u low -i /usr/share/pixmaps/mutt.xpm 
   ↪-t 5000 'New Mail' '$output'");
   system ("blink -s -r $total");
}
else {
   system ("blink -r 0");
}

The great thing about using a keyboard LED for notifications is that you end up noticing it out of the corner of your eye, especially if you left your computer for a minute, yet it won't steal your focus completely if you are working. I also like that I can tell how many new messages I have at a glance. If you want to extend the script further, I'd recommend separating your folders into regular and high priorities, and make the script blink different LEDs depending on which folders have new mail. If you have multiple e-mail accounts, you might even want multiple versions of the script with an LED assigned to each account. I'd say the possibilities are endless, but they aren't. You have only three LEDs to play with.

In this column, I've mostly mentioned checking for e-mail as a candidate for desktop notifications, but there are any number of other things for which you might want notifications, such as system temperature, LEDs triggered by the local mail or printer queues, notices triggered by RSS feed (or dare I say it, Twitter) updates, or even desktop notifications tripped by your server monitoring system. Why keep all those dæmons running silently in the background when you can make them speak up when there's something to report?

Kyle Rankin is a Systems Architect in the San Francisco Bay Area and the author of a number of books, including The Official Ubuntu Server Book, Knoppix Hacks and Ubuntu Hacks. He is currently the president of the North Bay Linux Users' Group.