【译文】用D-Bus控制你的Linux桌面系统

【译者CatUpp序】原文是 Koen Vervloesem 在2010年11月发表于 Linux Journal 的 Control Your Linux Desktop with D-Bus,感觉写得不错就翻译了一下。翻译得马马虎虎凑合着看吧(2014年03月15日)。

================================(以下是译文)================================

使用D-Bus,可以个性化并自动化你的桌面。

每个现代Linux桌面系统都使用 D-Bus —— 一个允许应用程序之间相互交互的系统。多亏了 D-Bus,你可以按照你的意愿来运作你的桌面。在这篇文章里我会阐述 D-Bus 能做到的事情。准备调整你的桌面系统了!

D-Bus (Desktop Bus) 是一个进程间通信(IPC)系统,它提供应用程序之间的通信机制。D-Bus是从头设计的,但是深受 KDE 的 DCOP (Desktop COmmunication Protocol) 的影响。D-Bus 已经无处不在了—— KDE 4 已经废弃 DCOP 而使用D-Bus,并且 GNOME 也从 GNOME 原生的 Bonobo 系统迁移至 D-Bus。所以,D-Bus已经成为桌面不可知的 IPC 机制。无论你使用那种桌面环境,使用 D-Bus 的软件将无缝地整合在你的桌面。D-Bus是跨桌面项目 freedesktop.org 的一部分,并且 Red Hat 公司是主要贡献者。

在 D-Bus 框架中,程序需要注册才能为其他程序提供服务(service)。注册后,其他程序可以查看哪些程序可提供服务(service)。另外,例如一些系统服务,程序也可以注册被事件(event)服务,比如检测热交换(hot-swapping)硬件。

D-Bus 不允许进城之间的直接通信,进程之间的通信是通过总线(bus)来完成的。总线守护进程(bus daemon)在一个总线上管理(route)进程间的消息传递,进程间就是以此同时和一个或多个应用程序通信。每个应用程序可以发送消息到总线或者监听总线上的事件。

D-Bus往往创建两条总线:一条是优先级较高的系统总线、另一条是会话总线。系统总线允许进程间在正确的权限下进行系统级的通信,系统总线的主要用途是从硬件抽象层 (HAL, Hardware Abstraction Layer) 分发消息到相应的进程。这种情况发生于一些需要被通知的硬件事件,比如插入新的硬件设备或者打印机的打印队列改变了。当用户登陆的时候,另一条总线——会话总线才会被创建,它允许应用程序之间的通信。

小谈 D-Bus

每个使用D-Bus的应用程序会暴露一些对象(通常映射成内部的 GObject(GNOME的GLib物件系統,依赖于GLib库和libc库,译者注)或 C++,Python 对象)。一个应用程序可以发送消息给另一个应用程序的指定对象。D-Bus 的每个对象被标示为唯一路径名,类似于文件系统的路径。为了确保每个应用程序使用独一无二的路径名,D-Bus对象的路径名的前缀通常是开发者域名,例如 /org/kde 或 /com/redhat . Java程序使用包名来命名(如 org.sum)。D-Bus的路径由三部分组成:1.服务名, 2.对象路径, 3.接口 (稍后给出例子)。

至此,如何在你的应用程序提供或使用 D-Bus 服务呢?核心的 API 是C语言写的,比较低级,其实不是为了给应用程序直接使用的。不同的编程语言和环境会在此 API 的基础上提供绑定层,如 GLib,Qt,Python,Ruby,Perl和Mono。在此我不深入 C 或 GLib (GNOME 基础库) 编程,我会给一些用脚本语言(Python和Ruby)和 shell 脚本写的例子。

哪些应用程序使用 D-Bus?

freedesktop.org 项目官网有不完全列表,每个程序的总线名也列出来了。然而,你可以用一些好玩的工具在你的操作系统上找到总线名。例如,Qt有一个图形化的 D-Bus 浏览器—— qdbusviewer(图一),在Ubuntu系统中这个工具在 qt4-dev-tools 包中。虽然这是KDE的一部分,但是这个工具在其他桌面系统(包括GNOME)完美运行。

图一:在 GNOME 运行 QDBusViewer

当你运行 qdbusviewer 你可以看到两个标签:会话总线系统总线。每个标签内左侧面板显示服务列表。如果你点击服务名,右侧面板显示关于此服务的信息,如可用的方法信号。比如,若你点击服务 org.freedesktop.PowerManagement, 然后点击右侧面板通过 org/, freedesktop/ 和 PowerManagement/, 你可以浏览 D-Bus 的两部分路径:左侧面板 org.freedesktop.PowerManagement 是服务名,右侧面板的 org/freedesktop/PowerManagement 是对象路径

右侧面板的对象路径最后一部分有三个东西,这三个东西就叫做接口:org.freedesktop.DBus.Introspectable, org.freedesktop.DBus.Properties 和 org.freedesktop.PowerManagement. 每个接口实现一些方法信号,这些是你可以互动的东西。在此,我们关注 org.freedesktop.PowerManagement 接口(实现具体的电源管理动作)。当你点击它,你可以看到所有实现的方法和信号。若你点击 “挂起”方法,你的电脑会被挂起,然后当你按下电源按钮的时候才会唤醒。

像关机、重启、睡眠和挂起之类的方法会实现一些动作,其他的方法会返回一些系统信息。例如, org.freedesktop.PowerManagement 对象实现一些例如 GetLowBattery,GetOnBattery,CanShutdown 之类的方法。如果你的系统(笔记本)在用饱满的电池,点击 GetOnBattery 会在下面板显示 “Arguments: true”,但是你点击 GetLowBattery时下面板则显示 “Arguments: false”。

值得注意的是,qdbusviewer 仅仅显示当前注册的服务名。比如,若你没打开 Pidgin,qdbusviewer 就不会显示 Pidgin 服务。所以你在计算有多少可以使用的 D-Bus 服务时需要注意这一点。

如果你习惯用命令行,你也可以不用 qdbusviewer。CLI应用程序 qdbus 也可以展示相同的信息。如果你在终端运行 qdbus,你也可以列出会话总线的可用服务名。如果你运行 qdbus --system 会列出来系统总线的服务。如果你想要知道一个服务暴露的不同对象,可以运行:

$ qdbus org.freedesktop.PowerManagement 

/org 
/org/freedesktop 
/org/freedesktop/PowerManagement 
/org/freedesktop/PowerManagement/Backlight 
/org/freedesktop/PowerManagement/Inhibit 
/org/freedesktop/PowerManagement/Statistics

现在你想知道那个接口实现 /org/freedesktop/PowerManagement 对象的话,用命令:

$ qdbus org.freedesktop.PowerManagement \ 
               /org/freedesktop/PowerManagement

这样得到的列表和 qdbusviewer 一样。例如下面这一行:

method bool org.freedesktop.PowerManagement.GetOnBattery() 

bool意思是返回布尔值(true或false)。如果方法没有返回值,比如 org.freedesktop.PowerManagement.Suspend(), 这行的返回值就是void。

qdbus同样允许直接调用这些方法。比如,若你想调用 Suspend 方法,执行:

$ qdbus org.freedesktop.PowerManagement \ 
              /org/freedesktop/PowerManagement \ 
              org.freedesktop.PowerManagement.Suspend

在命令行操作 D-Bus

接下来的剩余篇幅我会演示一些主流应用程序暴露出的 D-Bus 功能,并写一些脚本和这些应用程序交互并且完成一些任务。但愿能带给你们创意灵感。我将会使用不同的工具和脚本语言来显示D-Bus的不同功能。

我已经提到第一种和 D-Bus 交互的程序:KDE的 qdbusviewer 和 qdbus。若你没用KDE,你可以用 CLI 程序dbus-send 和 dbus-monitor 分别来发送和监听 D-Bus 消息。例如,你可以用下面的命令来进入挂起模式(suspend mode):

$ dbus-send --dest=org.freedesktop.PowerManagement \
                                /org/freedesktop/PowerManagement \
                                org.freedesktop.PowerManagement.Suspend

正如你所见,dbus-send 调用方式和 qdbus 差不多。唯一区别是你要用 --dest 参数来指定服务名。我们来看些其他新的东西。当你在浏览器看 YouTube 的一段长视频时会触发屏幕保护程序(screensaver),因为在操作系统中 Flash 插件不会和你系统的其他部分通信(原文:the Flash plugin doesn‘t communicate with the rest of your system,译者附)。通过 D-Bus,你可以阻止这恼人的行为。充满魔力的命令就是:

$ dbus-send --print-reply \
                     --dest=org.gnome.ScreenSaver / \
                      org.gnome.ScreenSaver.Inhibit \
                        string:"YouTube" \
                        string:"Inhibit Screensaver"

用这命令,你可以调用 org.gnome.ScreenSaver 接口的 Inhibit 方法(在Ubuntu 12.04 64-bit 上找不到 Inhibit 方法,译者注)。第一个参数是应用程序的名称。在此我使用 “YouTube”,它可以是任意名。第二个参数是禁止屏幕保护程序的原因。dbus-send 需要在参数前指明参数类型,如 string, boolean, int16 等等。两个参数都是 strings 类型。我还需要这命令的返回值(uint32 的 cookie)所以还有参数  --print-reply。如果你想要取消禁止屏幕保护程序,你还得通过下面的命令参数来发送 cookie:

$ dbus-send --dest=org.gnome.ScreenSaver / \
                                org.gnome.ScreenSaver.UnInhibit \
                                 uint32:1234567890

用这两个命令就可以 hack 自己个人的“禁止屏幕保护程序” shell 脚本。注意:在第一个命令运行的时候返回的 cookie 才是真实的,然后替换掉第二个命令的 cookie。

当你调试 D-Bus 脚本或观察其他 D-Bus 应用程序的方法和信号,CLI 的 dbus-monitor 很实用。赶紧在终端中使用吧,你可以看到所有 D-Bus 活动。对于实时地看所有 D-Bus 活动,dbus-monitor 的确好用。比如你网络挂了,你可以在 dbus-monitor 看消息是如何送到 D-Bus 的总线上的。于是乎在相同事件下,你就知道监听到了哪些信号或者调用了哪些方法。

dbus-monitor 也允许你指定一些参数:

$ dbus-monitor --system "interface=‘org.freedesktop.NetworkManager‘"

这样就会监听所有 NetworkManager 事件(因为 NetworkManager 走系统总线所以需要参数 --system argument)

关于 Liferea Feed Reader 的脚本

Liferea Feed Reader 有一些小而有趣的 D-Bus 方法。最有趣的是 Subscribe 方法,它允许你从其他应用程序添加订阅到 Liferea。其中之一就是 FeedBag,它是一个 Firefox 扩展,它可以修改浏览器地址栏的订阅按钮:点击按钮时会向 Liferea 添加一个提交而不是向 Live Bookmark 添加提交。这是因为 FeedBag 调用 org.gnome.feed.Reader.Subscribe 方法。你可以在终端下实现同样的功能:

$ feed="http://feeds2.feedburner.com/linuxjournalcom?format=xml"
$ dbus-send --dest=org.gnome.feed.Reader \
                               /org/gnome/feed/Reader \
                                org.gnome.feed.Reader.Subscribe\
                                     string:"$feed"

Liferea 提供一个脚本 liferea-add-feed 就是干上面的命令,但它包含一些错误处理机制。

Liferea 也通过 D-Bus 暴露一些信息,如果你有其他非 Liferea 的通知区域的窗口管理器的话就会非常有趣。然后,你可以策划你自己的通知系统——只需在 Liferea 请求一些新的未读和已读的项目然后显示输出:

$ dbus-send --print-reply \
                     --dest=org.gnome.feed.Reader \
                     /org/gnome/feed/Reader \
                     org.gnome.feed.Reader.GetNewItems
$ dbus-send --print-reply \
                     --dest=org.gnome.feed.Reader \
                                /org/gnome/feed/Reader \
                                 org.gnome.feed.Reader.GetUnreadItems

不在电脑前

若你想做一些复杂的任务而不是简单调用单一方法,你可以写包含 dbus-send 命令的 shell 脚本或使用高级语言来完成。Python, Ruby 和 Java 等语言都绑定了 D-Bus。

下一个例子中我将实现一个 Python 脚本——让你的 Pidgin 状态显示“不在电脑前”(若你的屏幕保护程序处于活跃状态的话)。要点有二:等待屏幕保护程序传来的信号,然后调用 Pidgin 的方法。脚本见 代码 1:

代码 1. pidgin_screensaver.py

#!/usr/bin/env python

def pidgin_status_func(state):
        obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
        pidgin = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
        status = pidgin.PurpleSavedstatusFind("afk")
        if status == 0:
                status = pidgin.PurpleSavedstatusNew("afk", 5)
        if state:
                pidgin.PurpleSavedstatusSetMessage(status, "Away from keyboard")
                pidgin.PurpleSavedstatusActivate(status)

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

bus.add_signal_receiver(pidgin_status_func,
                                         dbus_interface="org.gnome.ScreenSaver",
                                         signal_name="ActiveChanged")

loop = gobject.MainLoop()
loop.run()

我们来分析一下上面的代码。pidgin_status_func 函数设置你的 Pidgin 状态。它从会话总线得到 im/pidgin/purple/PurpleObject  对象和 im.pidgin.purple.PurpleInterface 接口,然后它调用接口的方法。之后,当检查到存在"afk"状态类型的时候创建新的 “saved status” 类型,不存在的话则创建"afk"类型(“afk”是“Away From Keyboard”的简称,"5" 则是状态类型)

然后,这个函数检查状态变量是不是 pidgin_status_func  的调用参数(稍后解释什么是参数)。如果参数是真,它会设置"afk"的状态消息为 “不在电脑前” 然后激活新的状态。这样,Pidgin显示状态 "afk" 的时候有状态消息 "不在电脑前"(Away from the Keyboard)。

现在你需要调用这个函数了(前提是屏幕保护程序已经激活)。然后开始 dbus 的主循环并且连接会话总线。接着添加一个信号接收器来监听 org.gnome.ScreenSaver 接口的 ActiveChanged 信号——当信号出现就会调用 pidgin_status_func 函数。正如 ActiveChanged 信号有一个用来指示当前屏幕保护程序状态是否激活的 boolean 参数,你也在 pidgin_status_func 函数定义了状态参数。为了保持监听状态,脚本要保持不断循环。

Pidgin 有一个丰富的 D-Bus 接口,你可以用它做任何事情。我用下面的例子给你一些灵感!

大玩 D-Bus

我们来看另一个例子,这次用 Ruby。我们来创建一个能够把当前 Rhythmbox 播放的歌曲作为 Pidgin 状态的脚本。

代码 2. pidgin_rhythmbox.rb

#!/usr/bin/env ruby

require ‘dbus‘

bus = DBus::SessionBus.instance
rhythmbox = bus.service("org.gnome.Rhythmbox")
player = rhythmbox.object("/org/gnome/Rhythmbox/Player")
player.introspect
player.default_iface = "org.gnome.Rhythmbox.Player"

pidgin = bus.service("im.pidgin.purple.PurpleService")
purple = pidgin.object("/im/pidgin/purple/PurpleObject")
purple.introspect
purple.default_iface = "im.pidgin.purple.PurpleInterface"

player.on_signal("playingUriChanged") do |uri|
    status = purple.PurpleSavedstatusFind("rhythmbox").first
    if status == 0
        status = purple.PurpleSavedstatusNew("rhythmbox", 2).first
    end
    purple.PurpleSavedstatusSetMessage(status, uri.to_s)
    purple.PurpleSavedstatusActivate(status)
end

这里你可以看到和前面 Python 脚本同样类型的命令:打开一个 D-Bus 会话,定义 D-Bus 服务、对象和接口,然后定义一个信号接收器。接着进入主循环保持监听 D-Bus 信号。

当然,这效果可能不够漂亮。比如,你再 Pidgin 显示的效果可能会是这首歌的路径名。有兴趣的读者可以提取这首歌相关的 ID3 标签而不是这首歌的路径名。

结论

现在你知道怎么用 D-Bus 调用也知道怎么处理 D-Bus 信号了,你可以开始自动化你的桌面任务了。如果你是 Linux 铁粉, D-Bus 不能不在你的字典里!

还有很多关于 D-Bus 的东西不能在这里展示了,但是除了 qdbusviewerqdbusdbus-send 和 dbus-monitor,你可以自己探索其他的东西。如果你想要创建其他基于 D-Bus 的复杂任务,用 Python 和 Ruby 都是不错的选择。你可以参考下列资源,发挥你的想象力。

如果你是软件开发者,你还需要关注注册服务。注册服务后,其他应用程序才可以通过 D-Bus 使用你的对象。

资源列表

D-Bus: www.freedesktop.org/wiki/Software/dbus

D-Bus Bindings: www.freedesktop.org/wiki/Software/DBusBindings

Python D-Bus Tutorial: dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

Ruby D-Bus Tutorial: trac.luon.net/data/ruby-dbus/tutorial/index.html

【译文】用D-Bus控制你的Linux桌面系统,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。