デスクトップ通知
関連記事
デスクトップ通知は、非同期に特定のイベントをユーザーに通知する小さくて控えめなポップアップダイアログです。
目次
- 1 Libnotify
- 2 通知サーバー
- 3 プログラミングでの使い方
- 4 ヒントとテクニック
- 5 トラブルシューティング
- 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
ヒントとテクニック
以前の通知を置き換える
ID がわかっている場合は、通知を置き換えることができます。新しい通知リクエストで同じ ID が指定されている場合、常に古い通知が置き換えられます。(上記の libnotify バインディングはこれを自動的に処理します。) 残念ながら、notify-send はこの ID を報告しないため、CLI でこれを行うには代替ツールが必要です。有効な CLI ツールの 1 つは、Notice-send.py Python スクリプトです。これは、追加の ID レポート機能と置換機能を備えた Notify-Send 構文を提供します。
ただし、一部の 通知サーバー (Notify-OSD など) では、notify-send で string:x-canonical-private-synchronous:
ヒントを使用して同じ結果を得ることができます。
たとえば、時間を表示する通知を取得するには:
while true; do date=$(date) notify-send "$date" -h string:x-canonical-private-synchronous:my-notification sleep 1 done
ボタンを含める、閉じる/クリックを把握する
Notice-send.py スクリプトを使用すると、アクションを使用してボタンを表示したり、通知のデフォルトアクションをリッスンしたりできます (通常、ユーザーがそれをクリックする)と閉じるアクション。action-icons ヒントが true に設定されており、通知デーモンがこれをサポートしている場合、ボタンにはテキストの代わりにアイコンが表示されます。スクリプトは、対応するイベントが発生したときに、コマンドラインにアクション ID または "close" を出力します。デフォルトのアクション (クリック時) をリッスンするには、アクション識別子 "default" を使用する必要があります。
ボタン上のアイコンの例:
notify-send.py "Buttons" "Do you like em?" --hint boolean:action-icons:true --action yes:face-cool no:face-sick
D-BUS サービスを備えば複数の通知サーバー
スタンドアロン セクションで説明されているように、ユーザーは通知サーバーを自動的に起動できるように D-Bus サービスを作成できます。一部の実装には、すでに D-Bus サービスファイルが含まれています。ただし、複数の通知サーバーがインストールされており、それらの一部にサービス ファイルが付属している場合、これにより問題が発生します。たとえば、目的のサーバーを明示的に指定せずに dunst と mako の両方をインストールすると、D-Bus がユーザーにどちらかを選択しますが、その決定はユーザーが制御できません。この状況を回避するには、使用するサービスを指すシンボリックリンク $XDG_DATA_DIR/dbus-1/services/org.freedesktop.Notifications.service
を作成して、使用するサービスをオーバーライドし、セッションを再起動します。
トラブルシューティング
アプリケーションがちょうど 1 分間ハングする
通知を表示しようとしているときにアプリケーションがハングする場合は、通知サービスが D-Bus サービスを通じてその可用性を誤って宣伝していることが原因である可能性があります。
たとえば、ユーザーが最近、plasma-workspace を必要とする KDE コンポーネントをインストールしたが、まだ XFCE を実行しているとします。この場合、KDE 通知機能が優先されますが、ユーザーはそれを実行していません。アプリケーションはサービスの待機中にハングし、タイムアウト後に xfce4-notifyd にフォールバックすることになります。
最も顕著なハングは、音量インジケーターのスクロール調整に起因する可能性があります。
この状況に陥った場合は、2 つの通知ハンドラーが必要です。
$ find /usr/share/dbus-1/services/ -name '*Notif*'
org.kde.plasma.Notifications.service org.xfce.xfce4-notifyd.Notifications.service
ジャーナル に見られるように、これら 2 つのうち、1 つは 1 分のタイムアウト後に定期的に失敗します。
# 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
D-BUS サービスを備えば複数の通知サーバー の説明に従って、使用するサービスを選択すると、問題が解決します。
参照
- Libnotify リファレンスマニュアル
- C サンプル
- Python 通知サンプル
- Python サンプル (フランス語の記事)