デスクトップ通知
関連記事
デスクトップ通知は、非同期に特定のイベントをユーザーに通知する小さくて控えめなポップアップダイアログです。
目次
- 1 Libnotify
- 2 通知サーバー
- 3 プログラミングでの使い方
- 4 Tips and tricks
- 5 Troubleshooting
- 6 参照
Libnotify
Libnotify は GTK+ や Qt アプリケーションのサポートを提供する Desktop Notifications Specification の実装で特定のデスクトップに依存していません: Evolution や Pidgin など多数のオープンソースアプリによって使用されています。Libnotify は公式リポジトリにある libnotify パッケージでインストールすることが可能です。
libnotify を使うには、通知サーバーをインストールする必要があります。
通知サーバー
ビルトイン
以下のデスクトップ環境では通知を表示するためにそれぞれ独自の実装を使っており、置き換えることができません。通知サーバーはログイン時に自動で起動し DBus によってアプリケーションからの通知を受け取ります。
- Cinnamon は通知サーバーを備えており、通知は画面の右上に表示されます。
- Enlightenment は Notification 拡張を通して通知サーバーを提供しています。通知オプションは設定が可能です。
- GNOME は通知サーバーを備えており、通知は画面の上部に表示されます。
- KDE は通知サーバーを備えており、通知は画面の右下に表示されます。
また、Deepin および GNOME Flashback も同様に通知サーバーを備えています。
スタンドアロン
他のデスクトップ環境では、ウィンドウマネージャやデスクトップ環境の自動実行を使って通知サーバーを起動する必要があります。
D-Bus で初めて呼ばれた時に通知サーバーを起動させるには、通知サーバー (notification-daemon パッケージなど) をインストールした後、以下のように設定を /usr/share/dbus-1/services
(またはユーザー個別に起動したい場合 $XDG_DATA_HOME/dbus-1/services
) に追加します:
org.freedesktop.Notifications.service
[D-BUS Service] Name=org.freedesktop.Notifications Exec=/usr/lib/notification-daemon-1.0/notification-daemon
通知サーバーは以下から選ぶことができます:
- Avant Window Navigator — AWN で使うことができる通知デーモンアプレット。
- Deadd Notification Center — Dnust にインスパイヤされた、通知センターが付いた通知デーモン。
- fnott — wlroots-based コンポジタのための軽量な Wayland 向けの通知デーモン。キーボードによって操作される。
- LXQt Notification Daemon — LXQt の通知サーバー。
- MATE Notification Daemon — MATE の通知サーバー。
- Notification Daemon — GNOME Flashback によって使われている通知サーバー。
- https://github.com/GNOME/notification-daemon || notification-daemon
/usr/lib/notification-daemon-1.0/notification-daemon
で手動で起動できます。
- Notify OSD — Unity の通知サーバー。
- statnot — 小さくて軽量な通知デーモン。ルートウィンドウのタイトルや標準出力、FIFO パイプなどに通知を出力できるので、タイル型ウィンドウマネージャと相性がとても良いです。
- sandsmark-notificationd — ミニマムな通知デーモン。ミュート機能をサポート。
- twmn — タイル型ウィンドウマネージャ向けの通知システム。
- wired — レイヤーブロックのカスタマイズ性が高い軽量な通知デーモン。Rust で書かれている。
- Xfce Notification Daemon — Xfce の通知サーバー。
プログラミングでの使い方
GObject-Introspection やバインディングを通して多くのプログラミング言語を使ったり、または bash を利用して簡単に libnotify でメッセージを表示することができます。
以下の例ではシンプルな "Hello world" の通知が表示されます。
Bash
- 依存パッケージ: libnotify
hello_world.sh
#!/bin/bash notify-send 'Hello world!' 'This is an example notification.' --icon=dialog-information
Boo
- 依存パッケージ: notify-sharp-3 (booAUR)
- ビルドするのに必要なパッケージ: booAUR
- ビルド:
booc hello_world.boo
- 実行:
mono hello_world.exe
(またはbooi hello_world.boo
)
hello_world.boo
import Notifications from "notify-sharp" Hello = Notification() Hello.Summary = "Hello world!" Hello.Body = "This is an example notification." Hello.IconName = "dialog-information" Hello.Show()
C
- 依存パッケージ: glib2
- ビルド:
gcc -o hello_world `pkg-config --cflags --libs gio-2.0` hello_world.c
hello_world.c
#include <gio/gio.h> int main() { GApplication *application = g_application_new ("hello.world", G_APPLICATION_FLAGS_NONE); g_application_register (application, NULL, NULL); GNotification *notification = g_notification_new ("Hello world!"); g_notification_set_body (notification, "This is an example notification."); GIcon *icon = g_themed_icon_new ("dialog-information"); g_notification_set_icon (notification, icon); g_application_send_notification (application, NULL, notification); g_object_unref (icon); g_object_unref (notification); g_object_unref (application); return 0; }
- 依存パッケージ: libnotify
- ビルド:
gcc -o hello_world `pkg-config --cflags --libs libnotify` hello_world.c
hello_world.c
#include <libnotify/notify.h> int main() { notify_init ("Hello world!"); NotifyNotification * Hello = notify_notification_new ("Hello world", "This is an example notification.", "dialog-information"); notify_notification_show (Hello, NULL); g_object_unref(G_OBJECT(Hello)); notify_uninit(); return 0; }
C++
- 依存パッケージ: glibmm
- ビルド:
g++ -o hello_world `pkg-config --cflags --libs giomm-2.4` hello_world.cc
hello_world.cc
#include <giomm-2.4/giomm.h> int main(int argc, char *argv[]) { auto Application = Gio::Application::create("hello.world", Gio::APPLICATION_FLAGS_NONE); Application->register_application(); auto Notification = Gio::Notification::create("Hello world"); Notification->set_body("This is an example notification."); auto Icon = Gio::ThemedIcon::create("dialog-information"); Notification->set_icon (Icon); Application->send_notification(Notification); return 0; }
- 依存パッケージ: libnotifymmAUR
- ビルド:
g++ -o hello_world `pkg-config --cflags --libs libnotifymm-1.0` hello_world.cc
hello_world.cc
#include <libnotifymm.h> int main(int argc, char *argv[]) { Notify::init("Hello world!"); Notify::Notification Hello("Hello world", "This is an example notification.", "dialog-information"); Hello.show(); return 0; }
C#
- 依存パッケージ: notify-sharp-3
- ビルド:
mcs -pkg:notify-sharp-3.0 hello_world.cs
- 実行:
mono hello_world.exe
hello_world.cs
using Notifications; public class HelloWorld { static void Main() { var Hello = new Notification(); Hello.Summary = "Hello world!"; Hello.Body = "This is an example notification."; Hello.IconName = "dialog-information"; Hello.Show(); } }
Cobra
- 依存パッケージ: notify-sharp-3
- ビルドするのに必要なパッケージ: cobraAUR
- ビルド:
cobra -c hello_world
- 実行:
mono hello_world.exe
hello_world.cobra
@args -pkg:notify-sharp-3.0 use Notifications class HelloWorld def main hello = Notification() hello.summary = "Hello world!" hello.body = "This is an example notification." hello.iconName = "dialog-information" hello.show
Crystal
- 依存パッケージ: woodruffw/notify.cr (shards から)
- ビルドするのに必要なパッケージ: crystal および shards
hello_world.cr
require "notify" notifier = Notify.new notifier.notify "Hello", body: "World!"
F#
- 依存パッケージ: notify-sharp-3
- ビルドするのに必要なパッケージ: fsharpAUR
- ビルド:
fsharpc -r:notify-sharp.dll -I:/usr/lib/mono/notify-sharp-3.0/ -I:/usr/lib/mono/gtk-sharp-3.0/ hello_world.fs
- 実行:
mono hello_world.exe
hello_world.fs
open Notifications let Hello = new Notification() Hello.Summary <- "Hello world!" Hello.Body <- "This is an example notification." Hello.IconName <- "dialog-information" Hello.Show()
Genie
hello_world.gs
uses GLib init var Application = new GLib.Application ("hello.world", GLib.ApplicationFlags.FLAGS_NONE); Application.register (); var Notification = new GLib.Notification ("Hello world"); Notification.set_body ("This is an example notification."); var Icon = new GLib.ThemedIcon ("dialog-information"); Notification.set_icon (Icon); Application.send_notification (null, Notification);
hello_world.gs
uses Notify init Notify.init ("Hello world") var Hello=new Notify.Notification ("Hello world!","This is an example notification.","dialog-information") Hello.show ()
Go
- 依存パッケージ: libnotify
- ビルドするのに必要なパッケージ: go-notify-gitAUR
- ビルド:
go build hello_world.go
- 実行:
go run hello_world.go
hello_world.go
package main import ("github.com/mqu/go-notify") func main() { notify.Init("Hello world") hello := notify.NotificationNew("Hello World!", "This is an example notification.","dialog-information") hello.Show() }
Groovy
- 依存パッケージ: groovy, java-gnomeAUR
- ビルド:
groovyc -cp /usr/share/java/gtk.jar HelloWorld.groovy && jar cfe HelloWorld.jar HelloWorld HelloWorld.class
- 実行:
java -cp /usr/share/groovy/embeddable/groovy-all.jar:/usr/share/java/gtk.jar:HelloWorld.jar HelloWorld
(またはgroovy -cp /usr/share/java/gtk.jar HelloWorld.groovy
)
HelloWorld.groovy
import org.gnome.gtk.* import org.gnome.notify.* Gtk.init() Notify.init("Hello world") def Hello = new Notification("Hello world!", "This is an example notification.", "dialog-information") Hello.show()
Haskell
- ビルドするのに必要なパッケージ: haskell-fdo-notify
- ビルド:
ghc hello_world
hello_world.hs
import DBus.Notify main = do client <- connectSession let hello = blankNote { summary="Hello world!", body=(Just $ Text "This is an example notification."), appImage=(Just $ Icon "dialog-information") } notification <- notify client hello return 0
IronPython
- 依存パッケージ: notify-sharp-3, ironpythonAUR
- 実行:
ipy hello_world.py
hello_world.py
import clr clr.AddReference('notify-sharp') import Notifications Hello = Notifications.Notification() Hello.Summary = "Hello world!" Hello.Body = "This is an example notification." Hello.IconName = "dialog-information" Hello.Show()
Java
- 依存パッケージ: java-gnomeAUR
- ビルドするのに必要なパッケージ: java-environment
- ビルド:
javac -cp /usr/share/java/gtk.jar HelloWorld.java && jar cfe HelloWorld.jar HelloWorld HelloWorld.class
- 実行:
java -cp /usr/share/java/gtk.jar:HelloWorld.jar HelloWorld
HelloWorld.java
import org.gnome.gtk.Gtk; import org.gnome.notify.Notify; import org.gnome.notify.Notification; public class HelloWorld { public static void main(String[] args) { Gtk.init(args); Notify.init("Hello world"); Notification Hello = new Notification("Hello world!", "This is an example notification.", "dialog-information"); Hello.show(); } }
JavaScript
- 依存パッケージ: gjs
hello_world.js
#!/usr/bin/gjs const Gio = imports.gi.Gio; var Application = new Gio.Application ({application_id: "hello.world"}); Application.register (null); var Notification = new Gio.Notification (); Notification.set_title ("Hello world"); Notification.set_body ("This is an example notification."); var Icon = new Gio.ThemedIcon ({name: "dialog-information"}); Notification.set_icon (Icon); Application.send_notification (null, Notification);
hello_world.js
#!/usr/bin/gjs const Notify = imports.gi.Notify; Notify.init ("Hello world"); var Hello=new Notify.Notification ({summary: "Hello world!", body: "This is an example notification.", "icon-name": "dialog-information"}); Hello.show ();
JRuby
- 依存パッケージ: java-gnomeAUR, jruby
- ビルド:
jrubyc hello_world.rb && jar cfe hello_world.jar hello_world hello_world.class
- 実行:
java -cp /opt/jruby/lib/jruby.jar:hello_world.jar hello_world
またはjruby hello_world.rb
hello_world.rb
require '/usr/share/java/gtk.jar' import Java::OrgGnomeGtk::Gtk import Java::OrgGnomeNotify::Notify import Java::OrgGnomeNotify::Notification Gtk.init(nil) Notify.init("Hello world") Hello = Notification.new("Hello world!", "This is an example notification.", "dialog-information") Hello.show
Jython
- 依存パッケージ: java-gnomeAUR, jython
- 実行:
jython -Dpython.path=/usr/share/java/gtk.jar hello_world.py
hello_world.py
from org.gnome.gtk import Gtk from org.gnome.notify import Notify, Notification Gtk.init(None) Notify.init("Hello world") Hello=Notification("Hello world!", "This is an example notification.", "dialog-information") Hello.show()
Lua
- 依存パッケージ: lua-lgi
hello_world.lua
#!/usr/bin/lua lgi = require 'lgi' Gio = lgi.require('Gio') Application = Gio.Application.new("hello.world",Gio.ApplicationFlags.FLAGS_NONE); Application:register(); Notification = Gio.Notification.new("Hello world"); Notification:set_body("This is an example notification."); Icon = Gio.ThemedIcon.new("dialog-information"); Notification:set_icon(Icon); Application:send_notification(nil, Notification);
hello_world.lua
#!/usr/bin/lua lgi = require 'lgi' Notify = lgi.require('Notify') Notify.init("Hello world") Hello=Notify.Notification.new("Hello world","This is an example notification.","dialog-information") Hello:show()</nowiki>
Nemerle
- 依存パッケージ: notify-sharp-3
- ビルドするのに必要なパッケージ: nemerleAUR
- ビルド:
ncc -pkg:notify-sharp-3.0 -out:hello_world.exe hello_world.n
- 実行:
mono hello_world.exe
hello_world.n
using Notifications; public class HelloWorld { static Main() : void { def Hello = Notification(); Hello.Summary = "Hello world!"; Hello.Body = "This is an example notification."; Hello.IconName = "dialog-information"; Hello.Show(); } }
Pascal
- 依存パッケージ: libnotify
- ビルドするのに必要なパッケージ: fpc, libnotify バインディング
- ビルド:
fpc hello_world
hello_world.pas
program hello_world; uses libnotify; var hello : PNotifyNotification; begin notify_init(argv[0]); hello := notify_notification_new ('Hello world', 'This is an example notification.', 'dialog-information'); notify_notification_show (hello, nil); end.
Perl
libnotify を使用
- 依存パッケージ: libnotify, perl-glib-object-introspection
hello_world.pl
#!/usr/bin/perl use Glib::Object::Introspection; Glib::Object::Introspection->setup ( basename => 'Notify', version => '0.7', package => 'Notify'); Notify->init; my $hello = Notify::Notification->new('Hello world!', "This is an example notification.", "dialog-information"); $hello->show;
直接 D-Bus 呼び出し
- 依存関係: perl-net-dbus
hello_world.pl
#!/usr/bin/perl use Net::DBus; my $bus = Net::DBus->session; my $svc = $bus->get_service('org.freedesktop.Notifications'); my $obj = $svc->get_object('/org/freedesktop/Notifications'); my $id = $obj->Notify('myapp', 0, 'dialog-information', 'Hello world!', 'This is an example notification.', [], {}, 0);
Python
- 依存パッケージ: python-gobject (または Python 2 なら python2-gobject)
hello_world.py
#!/usr/bin/python import gi gi.require_version('Gio', '2.0') from gi.repository import Gio Application=Gio.Application.new ("hello.world", Gio.ApplicationFlags.FLAGS_NONE); Application.register () Notification=Gio.Notification.new ("Hello world") Notification.set_body ("This is an example notification.") Icon=Gio.ThemedIcon.new ("dialog-information") Notification.set_icon (Icon) Application.send_notification (None, Notification)
- 依存パッケージ: libnotify, python-gobject (または Python 2 なら python2-gobject)
hello_world.py
#!/usr/bin/python import gi gi.require_version('Notify', '0.7') from gi.repository import Notify Notify.init ("Hello world") Hello = Notify.Notification.new ("Hello world","This is an example notification.","dialog-information") Hello.show ()
Ruby
- 依存パッケージ: libnotify, ruby-gir_ffiAUR
hello_world.rb
#!/usr/bin/ruby require 'gir_ffi' GirFFI.setup :Notify Notify.init("Hello world") Hello = Notify::Notification.new("Hello world!", "This is an example notification.", "dialog-information") Hello.show
Rust
notify-rust を使用。
- ビルドするのに必要なパッケージ: rust か rustup (Rust を参照)
- ビルド:
cargo build
- 実行:
target/debug/hello_world
またはcargo run
Cargo.toml
[package] name = "hello_world" version = "0.1.0" [dependencies] notify-rust = "^3"
src/main.rs
extern crate notify_rust; use notify_rust::Notification; fn main(){ Notification::new() .summary("Hello world") .body("This is an example notification.") .icon("dialog-information") .show().unwrap(); }
Scala
- 依存パッケージ: java-gnomeAUR (と scala)
- ビルドするのに必要なパッケージ: scala
- ビルド:
scalac -cp /usr/share/java/gtk.jar -d HelloWorld.jar HelloWorld.scala
- 実行:
java -cp /usr/share/java/gtk.jar:HelloWorld.jar HelloWorld
(またはscala -cp /usr/share/java/gtk.jar HelloWorld.scala
)
HelloWorld.scala
import org.gnome.gtk._ import org.gnome.notify._ object HelloWorld { def main(args: Array[String]) { Gtk.init(args) Notify.init("Hello world") var Hello = new Notification("Hello world!", "This is an example notification.", "dialog-information") Hello.show() } }
Vala
hello_world.vala
using GLib; public class HelloWorld { static void main () { var Application = new GLib.Application ("hello.world", GLib.ApplicationFlags.FLAGS_NONE); Application.register (); var Notification = new GLib.Notification ("Hello world"); Notification.set_body ("This is an example notification."); var Icon = new GLib.ThemedIcon ("dialog-information"); Notification.set_icon (Icon); Application.send_notification (null, Notification); } }
hello_world.vala
using Notify; public class HelloWorld { static void main () { Notify.init ("Hello world"); var Hello = new Notify.Notification("Hello world!", "This is an example notification.", "dialog-information"); Hello.show (); } }
Visual Basic .NET
- 依存パッケージ: notify-sharp-3
- ビルドするのに必要なパッケージ: mono-basicAUR
- ビルド:
vbnc -r:/usr/lib/mono/notify-sharp-3.0/notify-sharp.dll hello_world.vb
- 実行:
mono hello_world.exe
hello_world.vb
Imports Notifications Public Class Hello Public Shared Sub Main Dim Hello As New Notification Hello.Summary = "Hello world!" Hello.Body = "This is an example notification." Hello.IconName = "dialog-information" Hello.Show End Sub End Class
Tips and tricks
Replace previous notification
Notifications can be replaced if their ID is known; if a new notification request specifies the same ID, it will always replace the old notification. (The libnotify bindings shown above handle this automatically.) Unfortunately notify-send does not report this ID, so alternative tools are required to do this on CLI. One capable CLI-tool is the notify-send.py python script, which provides notify-send syntax with additional ID-reporting and replacing capabilities.
However, with some notification servers (such as Notify-OSD), you can use the string:x-canonical-private-synchronous:
hint with notify-send to achieve the same result.
For example, to get a notification displaying time:
while true; do date=$(date) notify-send "$date" -h string:x-canonical-private-synchronous:my-notification sleep 1 done
Include Buttons or listen for close/on-click of the notification
With the notify-send.py script, actions can be used to display buttons or to listen for the default-action of the notification (usually, when the user clicks on it) and the close-action. When the action-icons hint is set to true and the notification daemon supports this, the buttons will display icons instead of text. The script prints the action identifier or "close" to the command line when the corresponding event has occured. To listen for the default action (on-click), one has to use the action-identfier "default".
Example with icons on buttons:
notify-send.py "Buttons" "Do you like em?" --hint boolean:action-icons:true --action yes:face-cool no:face-sick
Multiple notification servers with D-Bus services
As described in the section Standalone, users can create a D-Bus service so that a notification server can be launched automatically. Some implementations already include the D-Bus service files. However, this causes a problem when multiple notification servers are installed and when some of them come with the service files. For example, installing both dunst and mako without explicitly specifying the desired server, D-Bus then chooses one for the users, and the decision is out of users' control. To avoid the situation, you can override the service used by creating the symbolic link $XDG_DATA_DIR/dbus-1/services/org.freedesktop.Notifications.service
pointing to the service you want to use, and then restart the session.
Troubleshooting
Applications hanging for exactly one minute
If applications hang when attempting to show notifications, it might be because of a notification service falsely advertising its availability through the D-Bus service.
For instance, suppose a user recently installed a KDE component that requires plasma-workspace, but the user is still running XFCE. In this case, the KDE notifier will be prioritized, but the user is not running it. The application will hang while waiting for the service, and only after a timeout will it fall back to xfce4-notifyd.
The most noticeable hanging might come from the volume indicator scroll adjustment.
If you are in this situation, you should have two notification handlers:
$ find /usr/share/dbus-1/services/ -name '*Notif*'
org.kde.plasma.Notifications.service org.xfce.xfce4-notifyd.Notifications.service
Of those two, one fails regularly after a 1-minute timeout, as seen in the journal:
# journalctl -g notif
[ press End to go to the end of the log ] Jul 01 09:40:49 laptop dbus-daemon[866]: [session uid=1000 pid=866] Activating service name='org.freedesktop.Notifications' requested by ':1.193' (uid=1000 pid=5432 comm="/usr/lib/xfce4/panel/wrapper-2.0 /usr/lib/xfce4/pa") Jul 01 09:41:49 laptop plasma_waitforname[6093]: org.kde.knotifications: WaitForName: Service was not registered within timeout Jul 01 09:41:49 laptop dbus-daemon[866]: [session uid=1000 pid=866] Activated service 'org.freedesktop.Notifications' failed: Process org.freedesktop.Notifications exited with status 1
Choosing the service you want to use as described in #Multiple notification servers with D-Bus services will fix the problem.
参照
- Libnotify リファレンスマニュアル
- C サンプル
- Python サンプル (フランス語の記事)