这部分的内容:
- GObject介绍
- 使用XML文件定义D-Bus接口
- 自动生成proxy/stub代码
- 创建一个简单的D-Bus对象
- 通过D-Bus发布一个GType类型
- 客户端如何使用Glib封装过的D-Bus
- D-Bus的自省功能
GObject介绍
为了在运行时把GTK+ widgets绑定到解释语言,一些牛人就用C语言实现了相对难以理解的面向对象的机制。根据你个人的爱好,你可以把这种面向对象称之为GObject或者GType。GType是GObject的底层基础。GObject/GType是Glib的一部分,并且单独编译成一个库:libgobject,对于GObject的详细介绍,大家可以到网上google一下,另外还要仔细阅读其代码,非常巧妙的实现!
这里的例子:实现一个非继承的类,类的接口去访问和修改两个私有的成员变量:value1和vaule2, value1是32位的整数,value2是一个gdouble类型的数据。
我们需要实现类构造函数和对象构造函数。这里,这两个构造函数都比较简短。如果你了解C++,你会发现这里怎么有两个构造函数呢?C++只有一个啊!不错,这就是GObject别扭的地方。慢慢你就会习惯的,当你很喜欢它后,你可能觉得C++是个另类。因为你了解C++的构造函数比较早,这个晚了些时候。
使用XML文件定义D-Bus接口:
由于我们主要的目的是做一个可以在D-Bus中使用的对象,因此我们从一个最简单的地方入手,通过dbus-binding-tool工具,这个工具会自动由XML文件生成client和server端的代码。我们就是把需要的接口安装XML语法定义在一个XML文件中就行了。
定义“方法”,即函数:用method来包含,其name是指定这个函数的名字,这个指定的名字会拷贝到生成的stub代码里面;函数method带一个参数:new_value, 用<arg />包含。其中对于参数类型(type)的的形式,需要参考D-Bus的参数类型定义。
<!-- setvalue1(int newValue): sets value1 -->
<method name="setvalue1">
<arg type="i" name="new_value" direction="in"/>
</method>
// direction表示入口参数还是出口参数,in:入口; out: 出口参数,如果不指定in或者out, 默认为in;
// type 的指定要参考D-Bus的类型定义,见下表;
其中,最难搞的就是要严格指定参数(arg)的类型(type),正是这个type来定义参数的数据类型,D-Bus定义的参数类型如下:
|
Conventional Name
|
Code
|
Description
|
|
INVALID
|
0 (ASCII NUL)
|
Not a valid type code, used to terminate signatures
|
|
BYTE
|
121 (ASCII 'y')
|
8-bit unsigned integer
|
|
BOOLEAN
|
98 (ASCII 'b')
|
Boolean value, 0 is FALSE and 1 is TRUE. Everything else is invalid.
|
|
INT16
|
110 (ASCII 'n')
|
16-bit signed integer
|
|
UINT16
|
113 (ASCII 'q')
|
16-bit unsigned integer
|
|
INT32
|
105 (ASCII 'i')
|
32-bit signed integer
|
|
UINT32
|
117 (ASCII 'u')
|
32-bit unsigned integer
|
|
INT64
|
120 (ASCII 'x')
|
64-bit signed integer
|
|
UINT64
|
116 (ASCII 't')
|
64-bit unsigned integer
|
|
DOUBLE
|
100 (ASCII 'd')
|
IEEE 754 double
|
|
STRING
|
115 (ASCII 's')
|
UTF-8 string (must be valid UTF-8). Must be nul terminated and contain no other nul bytes.
|
|
OBJECT_PATH
|
111 (ASCII 'o')
|
Name of an object instance
|
|
SIGNATURE
|
103 (ASCII 'g')
|
A type signature
|
|
ARRAY
|
97 (ASCII 'a')
|
Array
|
|
STRUCT
|
114 (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')')
|
Struct
|
|
VARIANT
|
118 (ASCII 'v')
|
Variant type (the type of the value is part of the value itself)
|
|
DICT_ENTRY
|
101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}')
|
Entry in a dict or map (array of key-value pairs)
|
我用蓝色标出的是比较常用的,你要根据你参数的需要,把上述第二列的类型码写到XML文件中去。开始你可能不习惯,慢慢就好了。
另外,D-Bus本身并不限制返回参数的个数,但是C语言只支持一个返回参数,因此如果你需要把其它需要携带回来的参数当作出口参数处理。其它的一些高级语言并不像C语言这样有限制。
下面是D-Bus函数所能支持的参数类型:(括号中是Glib对应的类型):
- b: boolean (gboolean)
- y: 8-bit unsigned integer (guint8)
- q/n: 16-bit unsigned/signed integer (guint16/gint16)
- u/i: 32-bit unsigned/signed integer (guint32/gint32)
- t/x: 64-bit unsigned/signed integer (guint64/gint64)
- d: IEEE 754 double precision floating point number (gdouble)
- s: UTF-8 encoded text string with NUL termination (only one NUL allowed) (gchar* with additional restrictions)
- a: Array of the following type specification (case-dependent)
- o/g/r/(/)/v/e/{/}: Complex types, please see the official D-Bus documentation on type signatures.
从这个列表可以看出,我们上面定义的函数:setvalue1有一个32位整形参数(new_value).这里定义的参数名称:new_value将会影响到所生成的stub代码,对于生成文档和D-Bus自省是非常有用的。
下面我们再定义另外一个函数:getvalue1, 这个函数用于返回当前对象的整数成员的值,没有入口参数,只有出口参数: cur_value。具体定义见下:
<!-- getvalue1(): returns the first value (int) -->
<method name="getvalue1">
<arg type="i" name="cur_value" direction="out"/>
</method>
我们已经知道,D-Bus的method是隶属于interface的,就说一个interface可以有N个method, 在XML中,我们把这些method元素包含在interface元素中,借以表达这种隶属关系。这个interface的名字属性是可选的,你可以指定,也可以不指定,我们强烈推荐你写上这个名字,一是防止各个模块的interface重名,另外一个重要的功用是为了introspection.
再进一步,method隶属于interface, 那么interface又属于谁呢?隶属于object, 一个object可以有N个interface. 我们把interface元素包含在node元素内。在XML中,node是最顶层的元素了。我们这个例子里面,只实现了一个interface(binding tool会自动增加introspection接口的,因此不必在XML文件中指定),到此,我们就写完了一个最基本的XML文件,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<node>
<interface name="org.maemo.Value">
<!-- getvalue1(): returns the first value (int) -->
<method name="getvalue1">
<arg type="i" name="cur_value" direction="out"/>
</method>
<!-- setvalue1(int newValue): sets value1 -->
<method name="setvalue1">
<arg type="i" name="new_value" direction="in"/>
</method>
</interface>
</node>
再对上面的最基本的XML接口定义文件做些扩充:增加DTD扫描,其实这个可以不用。另外还有就是增加了2个函数定义:get_value2, set_value2。
如果你需要写XML文件,强烈建议你在一个模板基础上写,就是拿个没有基本语法错误的模板,然后增加修改你的接口,这样容易成功,不要白手起家,不要在这个XML文件上面做过多的纠缠。
<?xml version="1.0" encoding="UTF-8" ?>
<!-- This maemo code example is licensed under a MIT-style license,
that can be found in the file called "License" in the same
directory as this file.
Copyright (c) 2007 Nokia Corporation. All rights reserved. -->
<!-- If you keep the following DOCTYPE tag in your interface
specification, xmllint can fetch the DTD over the Internet
for validation automatically.//自动对你定义的接口、方法、参数进行扫描,检查其合法性,如果你没有联网,就不要加这个了 -->
<!DOCTYPE node PUBLIC
"-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
"http://standards.freedesktop.org/dbus/1.0/introspect.dtd">
<!-- This file defines the D-Bus interface for a simple object, that
will hold a simple state consisting of two values (one a 32-bit
integer, the other a double).
The interface name is "org.maemo.Value".
One known reference implementation is provided for it by the
"/GlobalValue" object found via a well-known name of
"org.maemo.Platdev_ex". -->
<node>
<interface name="org.maemo.Value">
<!-- Method definitions -->
<!-- getvalue1(): returns the first value (int) -->
<method name="getvalue1">
<!-- NOTE Naming arguments is not mandatory, but is recommended
so that D-Bus introspection tools are more useful.
Otherwise the arguments will be automatically named
"arg0", "arg1" and so on. -->
<arg type="i" name="cur_value" direction="out"/>
</method>
<!-- getvalue2(): returns the second value (double) -->
<method name="getvalue2">
<arg type="d" name="cur_value" direction="out"/>
</method>
<!-- setvalue1(int newValue): sets value1 -->
<method name="setvalue1">
<arg type="i" name="new_value" direction="in"/>
</method>
<!-- setvalue2(double newValue): sets value2 -->
<method name="setvalue2">
<arg type="d" name="new_value" direction="in"/>
</method>
</interface>
</node>
写完XML文件后,下面我们是不是可以使用D-Bus工具来生成代码了?别急!我们还要对XML文件做些自动检查。主要检查两个方面:是否符合XML1.0规范;验证XML结构(即:元素是否成对匹配)。结构验证的规则是由DTD(Document Type Definition)文档规定的。D-Bus规定的XML格式如下:
|
<!-- DTD for D-BUS Introspection data -->
<!-- (C) 2005-02-02 David A. Wheeler; released under the D-BUS licenses,
GNU GPL version 2 (or greater) and AFL 1.1 (or greater) -->
<!-- see D-BUS specification for documentation -->
<!ELEMENT node (interface*,node*)>
<!ATTLIST node name CDATA #REQUIRED>
<!ELEMENT interface (annotation*,method*,signal*,property*)>
<!ATTLIST interface name CDATA #REQUIRED>
<!ELEMENT method (annotation*,arg*)>
<!ATTLIST method name CDATA #REQUIRED>
<!ELEMENT arg EMPTY>
<!ATTLIST arg name CDATA #IMPLIED>
<!ATTLIST arg type CDATA #REQUIRED>
<!-- Method arguments SHOULD include "direction",
while signal and error arguments SHOULD not (since there's no point).
The DTD format can't express that subtlety. -->
<!ATTLIST arg direction (in|out) "in">
<!ELEMENT signal (arg,annotation)>
<!ATTLIST signal name CDATA #REQUIRED>
<!ELEMENT property (annotation)> <!-- AKA "attribute" -->
<!ATTLIST property name CDATA #REQUIRED>
<!ATTLIST property type CDATA #REQUIRED>
<!ATTLIST property access (read|write|readwrite) #REQUIRED>
<!ELEMENT annotation EMPTY> <!-- Generic metadata -->
<!ATTLIST annotation name CDATA #REQUIRED>
<!ATTLIST annotation value CDATA #REQUIRED>
|