Desktop通知サーバーについて調べた
ubuntuに標準で入ってる、「email届いたよ」「battery少なくなってるよ」などの通知を右上あたりに表示してくれるあの子
通知を送るには
$ notify-send 'hello world'
で済むけど、受け取る側を作りたかった
構造
図で示す
dbusでのprotocolはfreedesktop.orgが関与していて、特定の環境に依存していない
dbus-daemon
| ^
dbus | | dbus
v |
notification-server user-applications
libnotify
というライブラリはuser側のためのもの
server側に関してはdbusを叩くしかないらしい
dbus
dbus-send
やdbus-monitor
というコマンド、あるいはd-feet
というGUIツールを使うとよい
例えば以下のようにすれば、notify-send
が送ったメッセージが読める
$ dbus-monitor destination=org.freedesktop.Notifications &
$ notify-send foo bar
dbus-send
で送る構文は
$ dbus-send [--system] --dest=BUS_NAME [--type=method_call] [--print-reply] OBJECT_PATH INTERFACE [TYPE:VALUE ...]
例えば通知サーバの情報を得るには
$ dbus-send --dest=org.freedesktop.Notifications --type=method_call --print-reply /org/freedesktop/Notifications org.freedesktop.Notifications.GetServerInformation
method return sender=:1.195 -> dest=:1.202 reply_serial=2
string "notify-osd"
string "Canonical Ltd"
string "1.0"
string "1.1"
ただしdbus-send
はdict
の要素としてのvariant
が使えないので、通知を発生させることはできない1ので注意
api
仕様(Desktop Notifications Specification)のD-BUS Protocolの項
直接に通知が来るのがorg.freedesktop.Notifications.Notify
variant
notify-osdのorg.freedesktop.Notifications.Notify
の型はsusssasa{sv}i
2であるが、variant
をstring
で置き換えsusssasa{ss}i
としたものも動く
libnotify
が上手く処理してくれているのだと思う
code
以上を触りながら調べた際の副産物のコード
既存の通知サーバを殺して3から起動する
statnotから切り出したものなのでGPLv2
#!/usr/bin/python3
import gi.repository.GLib
import dbus
import dbus.service
import dbus.mainloop.glib
NOTIF_BUS_NAME = 'org.freedesktop.Notifications'
NOTIF_OBJECT_PATH = '/org/freedesktop/Notifications'
NOTIF_INTERFACE = 'org.freedesktop.Notifications'
class NotificationServer(dbus.service.Object):
_id = 0
@dbus.service.method(NOTIF_INTERFACE,
in_signature='susssasa{sv}i',
out_signature='u')
def Notify(self, app_name, replace_id, app_icon,
summary, body, actions, hints, expire_timeout):
if not replace_id:
self._id += 1
notification_id = self._id
print( { 'app_name' : app_name
, 'notification_id' : notification_id
, 'app_icon' : app_icon
, 'summary' : summary
, 'body' : body
, 'actions' : actions
, 'hints' : hints
, 'expire_timeout' : expire_timeout
} )
return notification_id
@dbus.service.method(NOTIF_INTERFACE, in_signature='', out_signature='as')
def GetCapabilities(self):
return ('body', )
@dbus.service.signal(NOTIF_INTERFACE, signature='uu')
def NotificationClosed(self, id_in, reason_in):
pass
@dbus.service.method(NOTIF_INTERFACE, in_signature='u', out_signature='')
def CloseNotification(self, id):
pass
@dbus.service.method(NOTIF_INTERFACE, in_signature='', out_signature='ssss')
def GetServerInformation(self):
return ('developping', 'http://localhost', '0.0.1', '1.2') # name vendor version spec_version
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
args = parser.parse_args()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
name = dbus.service.BusName(NOTIF_BUS_NAME, bus, do_not_queue=True) # don't remove the binding: to avoid to call dtor
NotificationServer(bus, NOTIF_OBJECT_PATH)
mainloop = gi.repository.GLib.MainLoop()
mainloop.run() # don't quit
参考
- Desktop notifications (日本語) - ArchWiki
- Desktop Notifications Specification (Version 1.2)
- D-Bus の存在を感じてみる
- Libnotify Reference Manual