NotificationDevelopmentGuidelines

Revision 28 as of 2009-02-26 20:25:28

Clear message

How to use libnotify (and be a good citizen)

To use libnotify correctly - first and foremost - means to stick to the f.d.o specification and query for notification-daemon capabilities before using them. Don't just assume they are there. Also checking a notifications-daemon's name is a good way to branch off to dedicated code-paths catering for one or the other. Complete self-contained sourcecode for C, Python and C# you find further down the page.

  • C code-fragments for querying capabilities

gboolean g_capabilities[CAP_MAX] = {FALSE, /* actions         */
                                    FALSE, /* body            */
                                    FALSE, /* body-hyperlinks */
                                    FALSE, /* body-imges      */
                                    FALSE, /* body-markup     */
                                    FALSE, /* icon-multi      */
                                    FALSE, /* icon-static     */
                                    FALSE, /* sound           */
                                    FALSE, /* image/svg+xml   */
                                    FALSE  /* append-hint     */};
...
void
set_cap (gpointer data,
         gpointer user_data)
{
        /* test for "actions" */
        if (!g_strcmp0 ("actions", (gchar*) data))
                g_capabilities[CAP_ACTIONS] = TRUE;

        /* test for "body" */
        if (!g_strcmp0 ("body", (gchar*) data))
                g_capabilities[CAP_BODY] = TRUE;
        ...
        /* test for "append" */
        if (!g_strcmp0 ("append", (gchar*) data))
                g_capabilities[CAP_APPEND] = TRUE;
}
...
void
walk_caps_list ()
{
        GList* caps_list;

        caps_list = notify_get_server_caps ();
        if (caps_list)
        {
                g_list_foreach (caps_list, set_cap, NULL);
                g_list_foreach (caps_list, (GFunc) g_free, NULL);
                g_list_free (caps_list);
        }
}
...
int
main (int    argc,
      char** argv)
{
        ...
        walk_caps_list ();
        ...
        if (g_capabilities[CAP_APPEND])
        {
                /* you're save to make use of the append hint */
                ...
        }
        else
        {
                /* do _not_ use the append hint */
                ...
        }
        ...
}
  • C code-fragments for checking name of daemon

gchar* name;
gchar* vendor;
gchar* version;
gchar* spec_version;

notify_get_server_info (&name, &vendor, &version, &spec_version);
...
if (!g_strcmp0 ("notify-osd", name))
        g_print ("You're very likely running Ubuntu 9.04.\n");

if (!g_strcmp0 ("notification-daemon", name))
        g_print ("Perhaps you're running Fedora or an earlier version of Ubuntu.");
...
g_free ((gpointer) name);
g_free ((gpointer) vendor);
g_free ((gpointer) version);
g_free ((gpointer) spec_version);
  • Python code-fragments for querying capabilities

capabilities = {'actions':             False,
                'body':                False,
                'body-hyperlinks':     False,
                'body-images':         False,
                'body-markup':         False,
                'icon-multi':          False,
                'icon-static':         False,
                'sound':               False,
                'image/svg+xml':       False,
                'append':              False}

caps = pynotify.get_server_caps ()
        if caps is None:
                print "Failed to receive server caps."
                sys.exit (1)

        for cap in caps:
                capabilities[cap] = True

...

if capabilities['append']:
        # you're save to use the append-hint
        ...
else:
        # do _not_ use the append-hint
        ...
  • Python code-fragments for checking name of daemon

info = pynotify.get_server_info ()

if info["name"] == "notify-osd":
        print "You're very likely running Ubuntu 9.04."
else:
        print "Perhaps you're running Fedora or an earlier version of Ubuntu."
  • C# code-fragments for querying capabilities

enum Capability {
        CAP_ACTIONS = 0,
        CAP_BODY,
        CAP_BODY_HYPERLINKS,
        CAP_BODY_IMAGES,
        CAP_BODY_MARKUP,
        CAP_ICON_MULTI,
        CAP_ICON_STATIC,
        CAP_SOUND,
        CAP_IMAGE_SVG,
        CAP_APPEND,
        CAP_MAX}

static bool[] m_capabilities = {false,  // actions
                                false,  // body
                                false,  // body-hyperlinks
                                false,  // body-imges
                                false,  // body-markup
                                false,  // icon-multi
                                false,  // icon-static
                                false,  // sound
                                false,  // image/svg+xml
                                false}; // append-hint

if (Global.Capabilities == null)
        return;

if (Array.IndexOf (Global.Capabilities, "append") > -1)
        m_capabilities[(int) Capability.CAP_APPEND] = true;
...
if (Array.IndexOf (Global.Capabilities, "append") > -1)
        m_capabilities[(int) Capability.CAP_APPEND] = true;

if (m_capabilities[(int) Capability.CAP_APPEND])
{
        // you're save to use the append-hint
        ...
}
else
{
        // do _not_ use the append-hint
        ...
}
  • C# code-fragments for checking name of daemon

if (Notifications.Global.ServerInformation.Name == "notify-osd")
{
        Console.WriteLine ("You're very likely running Ubuntu 9.04.");
}
else
        Console.WriteLine ("Perhaps you're running Fedora or an earlier version of Ubuntu.");

Target developer-audience

  • C

Example code from VLC (broken):

notify_notification_add_action(notification, "previous",
                               _("Previous"), Prev,
                               (gpointer*) p_intf, NULL);
notify_notification_add_action(notification, "next",
                               _("Next"), Next,
                               (gpointer*) p_intf, NULL );

Example code (fixed):

caps = notify_get_server_caps();
if(caps != NULL) {
  for(c = caps; c != NULL; c = c->next) {
    if(strcmp((char*)c->data, "actions") == 0 ) {
      supports_actions = TRUE;
      break;
    }
  }

  g_list_foreach(caps, (GFunc)g_free, NULL);
  g_list_free(caps);
}

/* Adds previous and next buttons in the notification if actions are supported. */
if(supports_actions) {
  notify_notification_add_action(notification, "previous",
                                 _("Previous"), Prev,
                                 (gpointer*) p_intf, NULL);
  notify_notification_add_action(notification, "next",
                                 _("Next"), Next,
                                 (gpointer*) p_intf, NULL);
}
  • Python
  • C#:

In C# you should check the existence of "actions" in Notifications.Global.Capabilities. This is easy with Array.IndexOf such as:

bool actions_supported = Notifications.Global.Capabilities != null &&
                    Array.IndexOf(Notifications.Global.Capabilities, "actions") > -1

Example code from Banshee:

Notification nf = new Notification (Catalog.GetString("Now Playing"),
                                    message, image, notif_area.Widget);
if (interface_action_service.PlaybackActions["NextAction"].Sensitive) {
  nf.AddAction("skip-song", Catalog.GetString("Skip this item"), OnSongSkipped);
}

nf.Show();

After notification fixes:

Notification nf = new Notification (Catalog.GetString ("Now Playing"),
                                    message, image, notif_area.Widget);
bool actions_supported = Notifications.Global.Capabilities != null &&
                     Array.IndexOf (Notifications.Global.Capabilities, "actions") > -1;
if (interface_action_service.PlaybackActions["NextAction"].Sensitive &&
    actions_supported) {
  nf.AddAction ("skip-song", Catalog.GetString("Skip this item"), OnSongSkipped);
}

nf.Show ();

Layout cases (with examples in C, Python and C#)

Everything not listed as a valid layout will lead to a "no-layout"-case (results in an empty notification-bubble). Also using non-existing (stock-)icon-names results in empty notification-bubbles. Common caues for the latter could be that the user has not set "Human" as the icon-theme and a notification is trying to use one of the new icon-name (see icons). The comment header of each sourcecode example contains compilation and run instructions. NOTE: The C#-examples don't format and display in thie MoinMoin wiki. You can only download them directly to your harddisk. Sorry for the inconvenience!

Icon-Summary-Body

icon-summary-body.png
example: IM-message

Icon-Summary

icon-summary.png
example: Wifi connection lost

Summary-Body

summary-body.png
example: a very simple notification-bubble

Summary-only

summary-only.png
This layout-case works, but is strongly discouraged. Avoid it if you can.

How to use the append-hint

Current configuration does not allow embedding of the file append-hint-example.ogg because of its mimetype audio/ogg.: append-hint-example.ogg
(Gee, I cannot get this ogg/theora-screencast correctly embedded. So much for supporting open standards.)
{{attachment:append-hint-example.ogg}}
For IM-clients (like pidgin) you can use the append-hint ("append").

How to update an existing notification-bubble

code

How do I get these slick icons

The user has to be using the Human icon-theme. Have a look at the icon-matrix. Or if a different icon-theme is selected, default icons satisfying the new icon-names are used. These will look different of course. The available icon-names are...

  • notification-audio-next
  • notification-audio-play
  • notification-audio-previous
  • notification-audio-volume-high
  • notification-audio-volume-low
  • notification-audio-volume-medium
  • notification-audio-volume-muted
  • notification-audio-volume-off
  • notification-battery-low
  • notification-device-eject
  • notification-device-firewire
  • notification-display-brightness-full
  • notification-display-brightness-high
  • notification-display-brightness-low
  • notification-display-brightness-medium
  • notification-display-brightness-off
  • notification-GSM-3G-full
  • notification-GSM-3G-high
  • notification-GSM-3G-low
  • notification-GSM-3G-medium
  • notification-GSM-3G-none
  • notification-GSM-disconnected
  • notification-GSM-EDGE-full
  • notification-GSM-EDGE-high
  • notification-GSM-EDGE-low
  • notification-GSM-EDGE-medium
  • notification-GSM-EDGE-none
  • notification-GSM-full
  • notification-GSM-H-full
  • notification-GSM-H-high
  • notification-GSM-high
  • notification-GSM-H-low
  • notification-GSM-H-medium
  • notification-GSM-H-none
  • notification-GSM-low
  • notification-GSM-medium
  • notification-GSM-none
  • notification-keyboard-brightness-full
  • notification-keyboard-brightness-high
  • notification-keyboard-brightness-low
  • notification-keyboard-brightness-medium
  • notification-keyboard-brightness-off
  • notification-message-email
  • notification-message-IM
  • notification-network-ethernet-connected
  • notification-network-ethernet-disconnected
  • notification-network-wireless-disconnected
  • notification-network-wireless-full
  • notification-network-wireless-high
  • notification-network-wireless-low
  • notification-network-wireless-medium
  • notification-network-wireless-none
  • notification-power-disconnected

... and are located under /usr/share/icons/Human/scalable/status. Until symlinks or updated icons for other icon-themes are installed (provided by updated packages) using these without the icon-theme being set to Human will fail to display the intended icon. The result is an empty notification-bubble. You can still of course use full file paths to .svg/.png/.jpg icons.

Help! No libnotify bindings for language X

If your program is not in written in C, Python or C# and there are no language bindings for libnotify available for your language of choice, you can still fall back to using the command-line tool "notify-send" (provided by the package: libnotify-bin) to trigger a notification. But be aware that you do not have access to all available features (e.g. you cannot make use of the append-hint or simply update an existing bubble). Furthermore your language does need to provide some system() call or method, which allows you to spawn external commands.

Here's a list of the four layout-cases you can achieve by just using the command-line tool "notify-send":

  • Icon-Summary-Body

notify-send "Cole Raby" "Hey pal, what's up with the party next weekend? Will you join me and Anna?" -i notification-message-IM
  • Icon-Summary

notify-send "WiFi connection lost" -i notification-network-wireless-disconnected
  • Summary-Body

notify-send "Totem" "This is a superfluous notification"
  • Summary-only

notify-send "Summary-only"

See examples for screenshots of each layout-case.

What will get you burnt

Do not use any of these...

  • expire-timeout
  • actions
  • html-markup (read: URL) in you summary- or body-text

If you do you'll either get a fallback GTK+-dialog or notification-bubble with no contents at all.

Guidelines for converting actions

Some applications were using the 'actions' facility for interacting with the user. The NotificationDesignGuidelines document exposes the different ways applications can use to interact with the user and advise to use a regular window, like a dialog box, or the main application window, to actually interact with the user.

How to prevent focus stealing?

If an application wants to get the attention of the user it should not disturb the user's workflow for unnecessary reasons. Unless it is a critical situation, it is advised not to raise the window in the foreground, and not to focus it either. The latter is meant to prevent focus stealing, which is particularly annoying when you're writing an email, discussing in a channel or typing a password, for example...

To prevent focus stealing, GTK developers should use gtk_window_set_focus_on_map(FALSE). See http://library.gnome.org/devel/gtk/unstable/GtkWindow.html#gtk-window-set-focus-on-map for reference.