Board logo

标题: 使用 D-BUS 连接桌面应用程序(2) [打印本页]

作者: look_w    时间: 2018-5-7 20:51     标题: 使用 D-BUS 连接桌面应用程序(2)

用例尽管 D-BUS 相对较新,但是却迅速地得到了采用。如前所述,可以构建具有 D-BUS 支持的         udev 以使得当热插拔(hot-plug)设备时它可以发送一个信号。任何应用程序都可以侦听这些事件并当接收到这些事件时执行动作。例如,gnome-volume-manager可以检测到 USB 存储棒的插入并自动挂载它;或者,当插入一个数码相机时它可以自动下载照片。      
一个更为有趣但很不实用的例子是 Jamboree 和 Ringaling 的结合。Jamboree 是一个简单的音乐播放器,它具有 D-BUS 接口,以使得它可以被告知播放、到下一首歌、改变音量等等。Ringaling 是一个小程序,它打开 /dev/ttyS0(一个串行端口)并观察接收到的内容。当 Ringaling 发现文本“RING”时,就通过D-BUS 告知 Jamboree 减小音量。最终的结果是,如果您的计算机上插入了一个调制解调器,而且电话铃响,则音乐音量就会为您减小。        正是计算机所追求的!      
代码示例现在,让我们来接触一些使用 D-BUS 代码的示例。
dbus-ping-send.c 每秒通过会话总线发送一个参数为字符串“Ping!”的信号。我使用 Glib 来管理总线,以使得我不需要自己来处理总线的连接细节。
清单 1. dbus-ping-send.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <glib.h>
#include <dbus/dbus-glib.h>
static gboolean send_ping (DBusConnection *bus);
int
main (int argc, char **argv)
{
  GMainLoop *loop;
  DBusConnection *bus;
  DBusError error;
  /* Create a new event loop to run in */
  loop = g_main_loop_new (NULL, FALSE);
  /* Get a connection to the session bus */
  dbus_error_init (&error);
  bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
  if (!bus) {
    g_warning ("Failed to connect to the D-BUS daemon: %s", error.message);
    dbus_error_free (&error);
    return 1;
  }
  /* Set up this connection to work in a GLib event loop */
  dbus_connection_setup_with_g_main (bus, NULL);
  /* Every second call send_ping() with the bus as an argument*/
  g_timeout_add (1000, (GSourceFunc)send_ping, bus);
  /* Start the event loop */
  g_main_loop_run (loop);
  return 0;
}
static gboolean
send_ping (DBusConnection *bus)
{
  DBusMessage *message;
  /* Create a new signal "Ping" on the "com.burtonini.dbus.Signal" interface,
   * from the object "/com/burtonini/dbus/ping". */
  message = dbus_message_new_signal ("/com/burtonini/dbus/ping",
                                     "com.burtonini.dbus.Signal", "Ping");
  /* Append the string "Ping!" to the signal */
  dbus_message_append_args (message,
                            DBUS_TYPE_STRING, "Ping!",
                            DBUS_TYPE_INVALID);
  /* Send the signal */
  dbus_connection_send (bus, message, NULL);
  /* Free the signal now we have finished with it */
  dbus_message_unref (message);
  /* Tell the user we send a signal */
  g_print("Ping!\n");
  /* Return TRUE to tell the event loop we want to be called again */
  return TRUE;
}




main 函数创建一个 GLib 事件循环,获得会话总线的一个连接,并将 D-BUS 事件处理集成到 Glib 事件循环之中。然后它创建了一个名为         send_ping 间隔为一秒的计时器,并启动事件循环。      
send_ping 构造一个来自于对象路径 /com/burtonini/dbus/ping 和接口         com.burtonini.dbus.Signal 的新的 Ping 信号。然后,字符串“Ping!”作为参数添加到信号中并通过总线发送。在标准输出中会打印一条消息以让用户知道发送了一个信号。      
当然,不应该向总线发送了信号而没有任何程序在侦听它们……于是我们需要:
清单 2.  dbus-ping-listen.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <glib.h>
#include <dbus/dbus-glib.h>
static DBusHandlerResult signal_filter
      (DBusConnection *connection, DBusMessage *message, void *user_data);
int
main (int argc, char **argv)
{
  GMainLoop *loop;
  DBusConnection *bus;
  DBusError error;
  loop = g_main_loop_new (NULL, FALSE);
  dbus_error_init (&error);
  bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
  if (!bus) {
    g_warning ("Failed to connect to the D-BUS daemon: %s", error.message);
    dbus_error_free (&error);
    return 1;
  }
  dbus_connection_setup_with_g_main (bus, NULL);
  /* listening to messages from all objects as no path is specified */
  dbus_bus_add_match (bus, "type='signal',interface='com.burtonini.dbus.Signal'");
  dbus_connection_add_filter (bus, signal_filter, loop, NULL);
  g_main_loop_run (loop);
  return 0;
}
static DBusHandlerResult
signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data)
{
  /* User data is the event loop we are running in */
  GMainLoop *loop = user_data;
  /* A signal from the bus saying we are about to be disconnected */
  if (dbus_message_is_signal
        (message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, "Disconnected")) {
    /* Tell the main loop to quit */
    g_main_loop_quit (loop);
    /* We have handled this message, don't pass it on */
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  /* A Ping signal on the com.burtonini.dbus.Signal interface */
  else if (dbus_message_is_signal (message, "com.burtonini.dbus.Signal", "Ping")) {
    DBusError error;
    char *s;
    dbus_error_init (&error);
    if (dbus_message_get_args
       (message, &error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) {
      g_print("Ping received: %s\n", s);
      dbus_free (s);
    } else {
      g_print("Ping received, but error getting message: %s\n", error.message);
      dbus_error_free (&error);
    }
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}




这个程序侦听 dbus-ping-send.c 正在发出的信号。         main 函数和前面一样启动,创建一个到总线的连接。然后它声明愿意当具有         com.burtonini.dbus.Signal 接口的信号被发送时得到通知,将         signal_filter 设置为通知函数,然后进入事件循环。      
当满足匹配的消息被发送时,         signal_func 会被调用。不过,它也将会收到来自总线本身的总线管理信号。要确定接收到消息时应该做些什么,仅仅需要检验消息头。如果消息是总线断开信号,则事件循环终止,因为侦听一个不存在的总线是没有意义的。(告知总线信号已经处理)。然后,将到来的消息与期望的消息相比较,如果成功,则解出参数并输出。如果到来的消息不是其中的任何一个,则告知总线没有处理那个消息。      
那两个示例使用了低层的 D-BUS 程序库,这个程序库是完全的,但是当您想创建服务和很多对象时,使用起来冗长得令人厌倦。有正在开发中的 C# 和 Python 包装器,提供了非常接近于 D-BUS 的逻辑模型的编程接口。作为一个示例,这里是用 Python 重新对 ping/listen 示例进行了更为精致的实现。由于 Python 绑定模拟了逻辑接口,所以不可能不通过一个服务来发送信号。所以这个例子也要创建一个服务:
清单 3. dbus-ping-send.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#! /usr/bin/env python
import gtk
import dbus
# Connect to the bus
bus = dbus.Bus()
# Create a service on the bus
service = dbus.Service("com.burtonini.dbus.SignalService", bus)
# Define a D-BUS object
class SignalObject(dbus.Object):
    def __init__(self, service):
        dbus.Object.__init__(self, "/", [], service)
# Create an instance of the object, which is part of the service
signal_object = SignalObject(service)
def send_ping():
    signal_object.broadcast_signal("com.burtonini.dbus.Signal", "Ping")
    print "Ping!"
    return gtk.TRUE
# Call send_ping every second to send the signal
gtk.timeout_add(1000, send_ping)
gtk.main()




代码大部分是不言而明的:获得一个到总线的连接并注册         com.burtonini.dbus.SignalService 服务。然后创建一个最小限度的 D-BUS 对象,这个对象每秒广播一个信号。代码比相应的         C 代码 更简单,但是需要做 Python 绑定的工作。(例如,没有方法向信号添加参数。)      
清单 4.  dbus-ping-listen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#! /usr/bin/env python
import gtk
import dbus
bus = dbus.Bus()
def signal_callback(interface, signal_name, service, path, message):
    print "Received signal %s from %s" % (signal_name, interface)
# Catch signals from a specific interface and object, and call signal_callback
# when they arrive.
bus.add_signal_receiver(signal_callback,
                        "com.burtonini.dbus.Signal", # Interface
                        None, # Any service
                        "/" # Path of sending object
                        )
# Enter the event loop, waiting for signals
gtk.main()




此代码比 dbus-ping-listen.c 中相应的         C 代码 更简明,也更容易读懂。此外,有些地方需要做绑定的工作(当调用         bus.add_signal_receiver 时,用户必须传入一个接口和一个对象路径;否则会创建不正常的匹配器)。这是一个微不足道的缺陷,一旦这个缺陷被修正,就可以去服务和对象路径参数除,这将进一步提高代码的可读性。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0