NotificationDevelopmentGuidelines
Contents
- How to use libnotify (and be a good citizen)
- Target developer-audience
- Layout cases (with examples in C, Python and C#)
- How to use the append-hint
- How to update an existing notification-bubble
- How do I get these slick icons
- Help! No libnotify bindings for language X
- What will get you burnt
- Guidelines for converting actions
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
example: IM-message
Icon-Summary
example: Wifi connection lost
Summary-Body
example: a very simple notification-bubble
Summary-only
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.)
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.