Maemo Linux手机平台系列分析:8 Maemo平台开发之 使用Glib绑定的D-Bus
2008-01-30 23:04
861 查看
使用Glib封装过的D-Bus
这部分的内容:
GObject介绍
使用XML文件定义D-Bus接口
自动生成proxy/stub代码
创建一个简单的D-Bus对象
通过D-Bus发布一个GType类型
客户端如何使用Glib封装过的D-Bus
D-Bus的自省功能
[align=left] [/align]
[align=left]GObject介绍[/align]
[align=left]为了在运行时把GTK+ widgets绑定到解释语言,一些牛人就用C语言实现了相对难以理解的面向对象的机制。根据你个人的爱好,你可以把这种面向对象称之为GObject或者GType。GType是GObject的底层基础。GObject/GType是Glib的一部分,并且单独编译成一个库:libgobject,对于GObject的详细介绍,大家可以到网上google一下,另外还要仔细阅读其代码,非常巧妙的实现![/align]
[align=left]这里的例子:实现一个非继承的类,类的接口去访问和修改两个私有的成员变量:value1和vaule2, value1是32位的整数,value2是一个gdouble类型的数据。[/align]
[align=left]我们需要实现类构造函数和对象构造函数。这里,这两个构造函数都比较简短。如果你了解C++,你会发现这里怎么有两个构造函数呢?C++只有一个啊!不错,这就是GObject别扭的地方。慢慢你就会习惯的,当你很喜欢它后,你可能觉得C++是个另类。因为你了解C++的构造函数比较早,这个晚了些时候。[/align]
[align=left] [/align]
[align=left]使用XML文件定义D-Bus接口:[/align]
[align=left] [/align]
[align=left]由于我们主要的目的是做一个可以在D-Bus中使用的对象,因此我们从一个最简单的地方入手,通过dbus-binding-tool工具,这个工具会自动由XML文件生成client和server端的代码。我们就是把需要的接口安装XML语法定义在一个XML文件中就行了。[/align]
[align=left]定义“方法”,即函数:用method来包含,其name是指定这个函数的名字,这个指定的名字会拷贝到生成的stub代码里面;函数method带一个参数:new_value, 用<arg />包含。其中对于参数类型(type)的的形式,需要参考D-Bus的参数类型定义。[/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left][/align]
[align=left]// direction表示入口参数还是出口参数,in:入口; out: 出口参数,如果不指定in或者out, 默认为in;[/align]
[align=left]// type 的指定要参考D-Bus的类型定义,见下表;[/align]
[align=left] [/align]
[align=left]其中,最难搞的就是要严格指定参数(arg)的类型(type),正是这个type来定义参数的数据类型,D-Bus定义的参数类型如下:[/align]
[align=left] [/align]
[align=left]我用蓝色标出的是比较常用的,你要根据你参数的需要,把上述第二列的类型码写到XML文件中去。开始你可能不习惯,慢慢就好了。[/align]
[align=left]另外,D-Bus本身并不限制返回参数的个数,但是C语言只支持一个返回参数,因此如果你需要把其它需要携带回来的参数当作出口参数处理。其它的一些高级语言并不像C语言这样有限制。[/align]
[align=left] [/align]
[align=left]下面是D-Bus函数所能支持的参数类型:(括号中是Glib对应的类型):[/align]
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.
[align=left]从这个列表可以看出,我们上面定义的函数:setvalue1有一个32位整形参数(new_value).这里定义的参数名称:new_value将会影响到所生成的stub代码,对于生成文档和D-Bus自省是非常有用的。[/align]
[align=left]下面我们再定义另外一个函数:getvalue1, 这个函数用于返回当前对象的整数成员的值,没有入口参数,只有出口参数: cur_value。具体定义见下:[/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left]我们已经知道,D-Bus的method是隶属于interface的,就说一个interface可以有N个method, 在XML中,我们把这些method元素包含在interface元素中,借以表达这种隶属关系。这个interface的名字属性是可选的,你可以指定,也可以不指定,我们强烈推荐你写上这个名字,一是防止各个模块的interface重名,另外一个重要的功用是为了introspection.[/align]
[align=left]再进一步,method隶属于interface, 那么interface又属于谁呢?隶属于object, 一个object可以有N个interface. 我们把interface元素包含在node元素内。在XML中,node是最顶层的元素了。我们这个例子里面,只实现了一个interface(binding tool会自动增加introspection接口的,因此不必在XML文件中指定),到此,我们就写完了一个最基本的XML文件,如下:[/align]
[align=left]<?xml version="1.0" encoding="UTF-8" ?>[/align]
[align=left]<node>[/align]
[align=left] <interface name="org.maemo.Value">[/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] </interface>[/align]
[align=left]</node>[/align]
[align=left] [/align]
[align=left]再对上面的最基本的XML接口定义文件做些扩充:增加DTD扫描,其实这个可以不用。另外还有就是增加了2个函数定义:get_value2, set_value2。[/align]
[align=left]如果你需要写XML文件,强烈建议你在一个模板基础上写,就是拿个没有基本语法错误的模板,然后增加修改你的接口,这样容易成功,不要白手起家,不要在这个XML文件上面做过多的纠缠。[/align]
[align=left]<?xml version="1.0" encoding="UTF-8" ?>[/align]
[align=left] [/align]
[align=left]<!-- This maemo code example is licensed under a MIT-style license,[/align]
[align=left] that can be found in the file called "License" in the same[/align]
[align=left] directory as this file.[/align]
[align=left] Copyright (c) 2007 Nokia Corporation. All rights reserved. -->[/align]
[align=left] [/align]
[align=left]<!-- If you keep the following DOCTYPE tag in your interface[/align]
[align=left] specification, xmllint can fetch the DTD over the Internet[/align]
[align=left] for validation automatically.//自动对你定义的接口、方法、参数进行扫描,检查其合法性,如果你没有联网,就不要加这个了 -->[/align]
[align=left]<!DOCTYPE node PUBLIC[/align]
[align=left] "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"[/align]
[align=left] "http://standards.freedesktop.org/dbus/1.0/introspect.dtd">[/align]
[align=left] [/align]
[align=left]<!-- This file defines the D-Bus interface for a simple object, that[/align]
[align=left] will hold a simple state consisting of two values (one a 32-bit[/align]
[align=left] integer, the other a double).[/align]
[align=left] [/align]
[align=left] The interface name is "org.maemo.Value".[/align]
[align=left] One known reference implementation is provided for it by the[/align]
[align=left] "/GlobalValue" object found via a well-known name of[/align]
[align=left] "org.maemo.Platdev_ex". -->[/align]
[align=left] [/align]
[align=left]<node>[/align]
[align=left] <interface name="org.maemo.Value">[/align]
[align=left] [/align]
[align=left] <!-- Method definitions -->[/align]
[align=left] [/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <!-- NOTE Naming arguments is not mandatory, but is recommended[/align]
[align=left] so that D-Bus introspection tools are more useful.[/align]
[align=left] Otherwise the arguments will be automatically named[/align]
[align=left] "arg0", "arg1" and so on. -->[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- getvalue2(): returns the second value (double) -->[/align]
[align=left] <method name="getvalue2">[/align]
[align=left] <arg type="d" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- setvalue2(double newValue): sets value2 -->[/align]
[align=left] <method name="setvalue2">[/align]
[align=left] <arg type="d" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] </interface>[/align]
[align=left]</node>[/align]
[align=left] [/align]
[align=left]写完XML文件后,下面我们是不是可以使用D-Bus工具来生成代码了?别急!我们还要对XML文件做些自动检查。主要检查两个方面:是否符合XML1.0规范;验证XML结构(即:元素是否成对匹配)。结构验证的规则是由DTD(Document Type Definition)文档规定的。D-Bus规定的XML格式如下:[/align]
[align=left]然后根据这个模板对你定义的XML文档进行规则检查。[/align]
[align=left]仅仅检查了DTD有效性,还不完美:因为DTD类型检查只是对语法做检查,并不能对语义/含义做检查。[/align]
[align=left]为了检查语义,我们使用另外一个工具:checkxml, 把它写到你的makefile中:[/align]
[align=left]# One extra target (which requires xmllint, from package libxml2-utils)[/align]
[align=left]# is available to verify the well-formedness and the structure of the[/align]
[align=left]# interface definition xml file.[/align]
[align=left]#[/align]
[align=left]# Use the 'checkxml' target to run the interface XML through xmllint[/align]
[align=left]# verification. You'll need to be connected to the Internet in order[/align]
[align=left]# for xmllint to retrieve the DTD from fd.o (unless you setup local[/align]
[align=left]# catalogs, which are not covered here).[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# Interface XML name (used in multiple targets)[/align]
[align=left]interface_xml := value-dbus-interface.xml[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# Special target to run DTD validation on the interface XML. Not run[/align]
[align=left]# automatically (since xmllint isn't always available and also needs[/align]
[align=left]# Internet connectivity).[/align]
[align=left]checkxml: $(interface_xml)[/align]
[align=left] @xmllint --valid --noout $<[/align]
[align=left] @echo $< checks out ok[/align]
[align=left]检查语义:[/align]
[align=left] [/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make checkxml[/align]
[align=left]value-dbus-interface.xml checks out ok[/align]
[align=left] [/align]
[align=left]为了验证DTD/checkxml能否起到作用,我们简单修改一下上面的XML文件,增加一个DTD中没有规定的节点</invalidElement> ,并且去掉一个method开始标签,看看效果:[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make checkxml[/align]
[align=left]value-dbus-interface.xml:36: element invalidElement: validity error :[/align]
[align=left] No declaration for element invalidElement[/align]
[align=left] </invalidElement>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:53: parser error :[/align]
[align=left] Opening and ending tag mismatch: method line 39 and interface[/align]
[align=left] </interface>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:54: parser error :[/align]
[align=left] Opening and ending tag mismatch: interface line 22 and node[/align]
[align=left]</node>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:55: parser error :[/align]
[align=left] Premature end of data in tag node line 21[/align]
[align=left] [/align]
[align=left]^[/align]
[align=left]make: *** [checkxml] Error 1[/align]
[align=left]上面,第一个被发现的错误(验证错误),是由于XML文件不符合DTD的规定导致的。第二个(解析错误)则是没有正确的书写XML。[/align]
[align=left]完成XML文件的两方面的检查后,我们开始生成代码。这个生成的代码我们叫它“glue”代码,什么意思呢?glue的本意是粘合,就是通过glue代码完成从Glib到D-Bus的映射关系:[/align]
[align=left]图:[/align]
[align=left][/align]
[align=left]由XML文件,运行dbus-binding-tool命令,辅以不同的参数,可以生成client侧和sever侧的stub代码,生成的client stub和server stub代码正好构成了Client/Server结构。由这两个stub来完成Glib与D-Bus的互通,由于应用程序使用Glib,进而完成应用程序与D-Bus的互通。我们把makefile改写一下:[/align]
[align=left]# Define a list of generated files so that they can be cleaned as well[/align]
[align=left]cleanfiles := value-client-stub.h /[/align]
[align=left] value-server-stub.h[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# If the interface XML changes, the respective stub interfaces will be[/align]
[align=left]# automatically regenerated. Normally this would also mean that your[/align]
[align=left]# builds would fail after this since you'd be missing implementation[/align]
[align=left]# code.[/align]
[align=left]# 这里的—prefix前缀会反映到你的stub代码中,会在生成的结构变量和函数名称中添加这个前缀,这个前缀大家不要省略,因为这里仅仅是生成头文件,实际的实现代码需要我们自己写,但是我们需要拷贝这里的声明到.c文件中,如果不指定这个前缀,并且有多个模块的话,会冲突的;[/align]
[align=left] [/align]
[align=left]# mode指定为glib-server就生成server stub代码;指定为glib-client[/align]
[align=left]# 则生成client stub代码[/align]
[align=left] [/align]
[align=left]# 生成sever stub代码[/align]
[align=left]value-server-stub.h: $(interface_xml)[/align]
[align=left] dbus-binding-tool --prefix=value_object --mode=glib-server /[/align]
[align=left] $< > $@[/align]
[align=left] [/align]
[align=left]# 生成client stub代码[/align]
[align=left]value-client-stub.h: $(interface_xml)[/align]
[align=left] dbus-binding-tool --prefix=value_object --mode=glib-client /[/align]
[align=left] $< > $@[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]clean:[/align]
[align=left] $(RM) $(targets) $(cleanfiles) *.o[/align]
[align=left]这里生成的client侧的代码是想通过Glib去访问对象的实现,而server侧代码则是实现对象。[/align]
[align=left]生成两个头文件:[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make value-server-stub.h value-client-stub.h[/align]
[align=left]dbus-binding-tool --prefix=value_object --mode=glib-server /[/align]
[align=left] value-dbus-interface.xml > value-server-stub.h[/align]
[align=left]dbus-binding-tool --prefix=value_object --mode=glib-client /[/align]
[align=left] value-dbus-interface.xml > value-client-stub.h[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > ls -la value*stub.h[/align]
[align=left]-rw-rw-r-- 1 user user 5184 Nov 21 14:02 value-client-stub.h[/align]
[align=left]-rw-rw-r-- 1 user user 10603 Nov 21 14:02 value-server-stub.h[/align]
[align=left]在开始实现实际的对象之前,我们先来瞄一眼生成的代码,看看到底什么妖蛾子。先看看server侧的stub文件:[/align]
[align=left]/* Generated by dbus-binding-tool; do not edit! */[/align]
[align=left] [/align]
[align=left] /*... Listing cut for brevity ...*/[/align]
[align=left] [/align]
[align=left]#include <dbus/dbus-glib.h>[/align]
[align=left]static const DBusGMethodInfo dbus_glib_value_object_methods[] = {[/align]
[align=left] { (GCallback) value_object_getvalue1,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER, 0 },[/align]
[align=left] { (GCallback) value_object_getvalue2,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER, 47 },[/align]
[align=left] { (GCallback) value_object_setvalue1,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__INT_POINTER, 94 },[/align]
[align=left] { (GCallback) value_object_setvalue2,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__DOUBLE_POINTER, 137 },[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]const DBusGObjectInfo dbus_glib_value_object_object_info = {[/align]
[align=left] 0,[/align]
[align=left] dbus_glib_value_object_methods,[/align]
[align=left] 4,[/align]
[align=left]"org.maemo.Value/0getvalue1/0S/0cur_value/0O/0F/0N/0i/0/0"[/align]
[align=left]"org.maemo.Value/0getvalue2/0S/0cur_value/0O/0F/0N/0d/0/0"[/align]
[align=left]"org.maemo.Value/0setvalue1/0S/0new_value/0I/0i/0/0"[/align]
[align=left]"org.maemo.Value/0setvalue2/0S/0new_value/0I/0d/0/0/0",[/align]
[align=left]"/0",[/align]
[align=left]"/0"[/align]
[align=left]};[/align]
[align=left][ The method table and object introspection data (glib-dbus-sync/value-server-stub.h) ][/align]
[align=left]在这个头文件中,生成的4个函数:value_object_getvalue1, value_object_getvalue2, value_object_setvalue1value_object_setvalue2. 需要我们去实现;在函数表dbus_glib_value_object_methods,由函数地址;marshalling函数(用于在Glib和D-Bus之间marshal数据)组成。[/align]
[align=left]Marshaling 的作用就是参数从一种形式转换为另外一种形式,主要是为了两种不同参数能互相兼容地传递。Marshalling技术是在大多数RPC中运用的。由于Glib和D-Bus各自有不同的类型系统,如果我们手动去写这种转换代码,是很乏味和无聊的。Binding tool可以帮忙解决这个问题。[/align]
[align=left]上述头文件中还有一个比较有意思的东西:xxx_object_info结构变量,这个结构变量是要注册给D-Bus daemon的,并且把它发布在通道(bus)中(然后client侧才能发现并调用其函数),这个过程其实和我们的生活很贴近,比如公交车上的电视广告:厂家把商品(看作object)的广告,送到广告发布商那里,广告商把该广告投放到公交车(看作D-Bus daemon)的电视中, 消费者(看作client)看到了该广告,可能购买这个产品(调用object的函数)。上面很长的字符串将会安装interface规范的样式进行压缩编码,对于函数名、参数等也是按照这样编码的。在这个头文件中还有很多自省的代码,这也是非常重要的部分,最后再解释。[/align]
[align=left]正如头文件的第一句提示:这是dbus-binding-tool自动生成的代码,请不要编辑它。因为每次编译都回自动生成这个文件,你如果修改了这里的头文件,会被覆盖掉的。所以最好不要修改。那如何使用这里的代码呢,你只要拷贝粘帖这里的代码到.c文件中就行了。[/align]
[align=left]下面我们就继续:实现在server stub函数表中调用的具体函数。We'll next continue with the server implementation for the functions that are called via the method table.[/align]
[align=left] [/align]
[align=left]定义一个对象,这个过程就是GObject的八股文:[/align]
[align=left]/* This defines the per-instance state.[/align]
[align=left] [/align]
[align=left] Each GObject must start with the 'parent' definition so that common[/align]
[align=left] operations that all GObjects support can be called on it. */[/align]
[align=left]typedef struct {[/align]
[align=left] /* The parent class object state. */[/align]
[align=left] GObject parent; //要继承自GObject,这样才能用GObject的便利[/align]
[align=left] /* Our first per-object state variable. */[/align]
[align=left] gint value1;[/align]
[align=left] /* Our second per-object state variable. */[/align]
[align=left] gdouble value2;[/align]
[align=left]} ValueObject;[/align]
[align=left] [/align]
[align=left]/* Per class state.[/align]
[align=left] [/align]
[align=left] For the first Value implementation we only have the bare minimum,[/align]
[align=left] that is, the common implementation for any GObject class. */[/align]
[align=left]typedef struct {[/align]
[align=left] /* The parent class state. */[/align]
[align=left] GObjectClass parent;[/align]
[align=left]} ValueObjectClass;[/align]
[align=left]下面接着定义一些宏,全是用G_TYPE_开头的,你不必知道的太多,会套用就行,如果想知道的很详细,可以参考:http://maemo.org/api_refs/4.0/gobject/index.html.[/align]
[align=left]/* Forward declaration of the function that will return the GType of[/align]
[align=left] the Value implementation. Not used in this program since we only[/align]
[align=left] need to push this over the D-Bus. */[/align]
[align=left]GType value_object_get_type(void);[/align]
[align=left] [/align]
[align=left]/* Macro for the above. It is common to define macros using the[/align]
[align=left] naming convention (seen below) for all GType implementations,[/align]
[align=left] and that's why we're going to do that here as well. */[/align]
[align=left] [/align]
[align=left]/* 下面这些定义其实就是“八股文”,固定的格式,你只要套用就行,等熟悉了后,你也不会一个个写,而是拷贝模板。[/align]
[align=left]*/[/align]
[align=left]#define VALUE_TYPE_OBJECT (value_object_get_type())[/align]
[align=left] [/align]
[align=left]#define VALUE_OBJECT(object) /[/align]
[align=left] (G_TYPE_CHECK_INSTANCE_CAST((object), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObject))[/align]
[align=left]#define VALUE_OBJECT_CLASS(klass) /[/align]
[align=left] (G_TYPE_CHECK_CLASS_CAST((klass), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObjectClass))[/align]
[align=left]#define VALUE_IS_OBJECT(object) /[/align]
[align=left] (G_TYPE_CHECK_INSTANCE_TYPE((object), /[/align]
[align=left] VALUE_TYPE_OBJECT))[/align]
[align=left]#define VALUE_IS_OBJECT_CLASS(klass) /[/align]
[align=left] (G_TYPE_CHECK_CLASS_TYPE((klass), /[/align]
[align=left] VALUE_TYPE_OBJECT))[/align]
[align=left]#define VALUE_OBJECT_GET_CLASS(obj) /[/align]
[align=left] (G_TYPE_INSTANCE_GET_CLASS((obj), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObjectClass))[/align]
[align=left] [/align]
[align=left]/* Utility macro to define the value_object GType structure. */[/align]
[align=left]G_DEFINE_TYPE(ValueObject, value_object, G_TYPE_OBJECT)[/align]
[align=left]宏定义完成后,下面我们就着手实现实例初始化和类初始化,类的初始化中我们调用了D-Bus的接口函数,把我们的对象信息注册给了D-Bus了,就是暴露在通道(bus)中了,这样,客户端就能找到这个对象了。[/align]
[align=left]/**[/align]
[align=left] * Since the stub generator will reference the functions from a call[/align]
[align=left] * table, the functions must be declared before the stub is included.[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue1(ValueObject* obj, gint* value_out,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_getvalue2(ValueObject* obj, gdouble* value_out,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_setvalue1(ValueObject* obj, gint value_in,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_setvalue2(ValueObject* obj, gdouble value_in,[/align]
[align=left] GError** error);[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Pull in the stub for the server side.[/align]
[align=left] */[/align]
[align=left]#include "value-server-stub.h"[/align]
[align=left] [/align]
[align=left] /*... Listing cut for brevity ...*/[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Per object initializer[/align]
[align=left] *[/align]
[align=left] * Only sets up internal state (both values set to zero)[/align]
[align=left] */[/align]
[align=left]static void value_object_init(ValueObject* obj) {[/align]
[align=left] dbg("Called");[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] obj->value1 = 0;[/align]
[align=left] obj->value2 = 0.0;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Per class initializer[/align]
[align=left] *[/align]
[align=left] * Registers the type into the GLib/D-Bus wrapper so that it may add[/align]
[align=left] * its own magic.[/align]
[align=left] */[/align]
[align=left]static void value_object_class_init(ValueObjectClass* klass) {[/align]
[align=left] [/align]
[align=left] dbg("Called");[/align]
[align=left] [/align]
[align=left] g_assert(klass != NULL);[/align]
[align=left] [/align]
[align=left] dbg("Binding to GLib/D-Bus");[/align]
[align=left] [/align]
[align=left] /* Time to bind this GType into the GLib/D-Bus wrappers.[/align]
[align=left] NOTE: This is not yet "publishing" the object on the D-Bus, but[/align]
[align=left] since it is only allowed to do this once per class[/align]
[align=left] creation, the safest place to put it is in the class[/align]
[align=left] initializer.[/align]
[align=left] Specifically, this function adds "method introspection[/align]
[align=left] data" to the class so that methods can be called over[/align]
[align=left] the D-Bus. */[/align]
[align=left] dbus_g_object_type_install_info(VALUE_TYPE_OBJECT,[/align]
[align=left] &dbus_glib_value_object_object_info);[/align]
[align=left] [/align]
[align=left] dbg("Done");[/align]
[align=left] /* All done. Class is ready to be used for instantiating objects */[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]下面我们实现具体的get /set函数,也就是object提供的methods。请注意:函数名和参数都要和stub头文件中的一模一样:[/align]
[align=left]/**[/align]
[align=left] * Function that gets called when someone tries to execute "setvalue1"[/align]
[align=left] * over the D-Bus. (Actually the marshaling code from the stubs gets[/align]
[align=left] * executed first, but they will eventually execute this function.)[/align]
[align=left] *[/align]
[align=left] * NOTE: If you change the name of this function, the generated[/align]
[align=left] * stubs will no longer find it! On the other hand, if you[/align]
[align=left] * decide to modify the interface XML, this is one of the places[/align]
[align=left] * that you'll have to modify as well.[/align]
[align=left] * This applies to the next four functions (including this one).[/align]
[align=left] */[/align]
[align=left]gboolean value_object_setvalue1(ValueObject* obj, gint valueIn,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (valueIn=%d)", valueIn);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] /* Change the value. */[/align]
[align=left] obj->value1 = valueIn;[/align]
[align=left] [/align]
[align=left] /* Return success to GLib/D-Bus wrappers. In this case we don't need[/align]
[align=left] to touch the supplied error pointer-pointer. */[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "setvalue2".[/align]
[align=left] * Other than this function operating with different type input[/align]
[align=left] * parameter (and different internal value), all the comments from[/align]
[align=left] * set_value1 apply here as well.[/align]
[align=left] */[/align]
[align=left]gboolean value_object_setvalue2(ValueObject* obj, gdouble valueIn,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (valueIn=%.3f)", valueIn);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] obj->value2 = valueIn;[/align]
[align=left] [/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "getvalue1".[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue1(ValueObject* obj, gint* valueOut,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (internal value1 is %d)", obj->value1);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] /* Check that the target pointer is not NULL.[/align]
[align=left] Even is the only caller for this will be the GLib-wrapper code,[/align]
[align=left] we cannot trust the stub generated code and should handle the[/align]
[align=left] situation. We will terminate with an error in this case.[/align]
[align=left] [/align]
[align=left] Another option would be to create a new GError, and store[/align]
[align=left] the error condition there. */[/align]
[align=left] g_assert(valueOut != NULL);[/align]
[align=left] [/align]
[align=left] /* Copy the current first value to caller specified memory. */[/align]
[align=left] *valueOut = obj->value1;[/align]
[align=left] [/align]
[align=left] /* Return success. */[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "getvalue2".[/align]
[align=left] * (Again, similar to "getvalue1").[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue2(ValueObject* obj, gdouble* valueOut,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (internal value2 is %.3f)", obj->value2);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] g_assert(valueOut != NULL);[/align]
[align=left] [/align]
[align=left] *valueOut = obj->value2;[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
这部分的内容:
GObject介绍
使用XML文件定义D-Bus接口
自动生成proxy/stub代码
创建一个简单的D-Bus对象
通过D-Bus发布一个GType类型
客户端如何使用Glib封装过的D-Bus
D-Bus的自省功能
[align=left] [/align]
[align=left]GObject介绍[/align]
[align=left]为了在运行时把GTK+ widgets绑定到解释语言,一些牛人就用C语言实现了相对难以理解的面向对象的机制。根据你个人的爱好,你可以把这种面向对象称之为GObject或者GType。GType是GObject的底层基础。GObject/GType是Glib的一部分,并且单独编译成一个库:libgobject,对于GObject的详细介绍,大家可以到网上google一下,另外还要仔细阅读其代码,非常巧妙的实现![/align]
[align=left]这里的例子:实现一个非继承的类,类的接口去访问和修改两个私有的成员变量:value1和vaule2, value1是32位的整数,value2是一个gdouble类型的数据。[/align]
[align=left]我们需要实现类构造函数和对象构造函数。这里,这两个构造函数都比较简短。如果你了解C++,你会发现这里怎么有两个构造函数呢?C++只有一个啊!不错,这就是GObject别扭的地方。慢慢你就会习惯的,当你很喜欢它后,你可能觉得C++是个另类。因为你了解C++的构造函数比较早,这个晚了些时候。[/align]
[align=left] [/align]
[align=left]使用XML文件定义D-Bus接口:[/align]
[align=left] [/align]
[align=left]由于我们主要的目的是做一个可以在D-Bus中使用的对象,因此我们从一个最简单的地方入手,通过dbus-binding-tool工具,这个工具会自动由XML文件生成client和server端的代码。我们就是把需要的接口安装XML语法定义在一个XML文件中就行了。[/align]
[align=left]定义“方法”,即函数:用method来包含,其name是指定这个函数的名字,这个指定的名字会拷贝到生成的stub代码里面;函数method带一个参数:new_value, 用<arg />包含。其中对于参数类型(type)的的形式,需要参考D-Bus的参数类型定义。[/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left][/align]
[align=left]// direction表示入口参数还是出口参数,in:入口; out: 出口参数,如果不指定in或者out, 默认为in;[/align]
[align=left]// type 的指定要参考D-Bus的类型定义,见下表;[/align]
[align=left] [/align]
[align=left]其中,最难搞的就是要严格指定参数(arg)的类型(type),正是这个type来定义参数的数据类型,D-Bus定义的参数类型如下:[/align]
[align=center]Conventional Name[/align] | [align=center]Code[/align] | [align=center]Description[/align] |
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 FALSEand 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) |
[align=left]我用蓝色标出的是比较常用的,你要根据你参数的需要,把上述第二列的类型码写到XML文件中去。开始你可能不习惯,慢慢就好了。[/align]
[align=left]另外,D-Bus本身并不限制返回参数的个数,但是C语言只支持一个返回参数,因此如果你需要把其它需要携带回来的参数当作出口参数处理。其它的一些高级语言并不像C语言这样有限制。[/align]
[align=left] [/align]
[align=left]下面是D-Bus函数所能支持的参数类型:(括号中是Glib对应的类型):[/align]
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.
[align=left]从这个列表可以看出,我们上面定义的函数:setvalue1有一个32位整形参数(new_value).这里定义的参数名称:new_value将会影响到所生成的stub代码,对于生成文档和D-Bus自省是非常有用的。[/align]
[align=left]下面我们再定义另外一个函数:getvalue1, 这个函数用于返回当前对象的整数成员的值,没有入口参数,只有出口参数: cur_value。具体定义见下:[/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left]我们已经知道,D-Bus的method是隶属于interface的,就说一个interface可以有N个method, 在XML中,我们把这些method元素包含在interface元素中,借以表达这种隶属关系。这个interface的名字属性是可选的,你可以指定,也可以不指定,我们强烈推荐你写上这个名字,一是防止各个模块的interface重名,另外一个重要的功用是为了introspection.[/align]
[align=left]再进一步,method隶属于interface, 那么interface又属于谁呢?隶属于object, 一个object可以有N个interface. 我们把interface元素包含在node元素内。在XML中,node是最顶层的元素了。我们这个例子里面,只实现了一个interface(binding tool会自动增加introspection接口的,因此不必在XML文件中指定),到此,我们就写完了一个最基本的XML文件,如下:[/align]
[align=left]<?xml version="1.0" encoding="UTF-8" ?>[/align]
[align=left]<node>[/align]
[align=left] <interface name="org.maemo.Value">[/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] </interface>[/align]
[align=left]</node>[/align]
[align=left] [/align]
[align=left]再对上面的最基本的XML接口定义文件做些扩充:增加DTD扫描,其实这个可以不用。另外还有就是增加了2个函数定义:get_value2, set_value2。[/align]
[align=left]如果你需要写XML文件,强烈建议你在一个模板基础上写,就是拿个没有基本语法错误的模板,然后增加修改你的接口,这样容易成功,不要白手起家,不要在这个XML文件上面做过多的纠缠。[/align]
[align=left]<?xml version="1.0" encoding="UTF-8" ?>[/align]
[align=left] [/align]
[align=left]<!-- This maemo code example is licensed under a MIT-style license,[/align]
[align=left] that can be found in the file called "License" in the same[/align]
[align=left] directory as this file.[/align]
[align=left] Copyright (c) 2007 Nokia Corporation. All rights reserved. -->[/align]
[align=left] [/align]
[align=left]<!-- If you keep the following DOCTYPE tag in your interface[/align]
[align=left] specification, xmllint can fetch the DTD over the Internet[/align]
[align=left] for validation automatically.//自动对你定义的接口、方法、参数进行扫描,检查其合法性,如果你没有联网,就不要加这个了 -->[/align]
[align=left]<!DOCTYPE node PUBLIC[/align]
[align=left] "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"[/align]
[align=left] "http://standards.freedesktop.org/dbus/1.0/introspect.dtd">[/align]
[align=left] [/align]
[align=left]<!-- This file defines the D-Bus interface for a simple object, that[/align]
[align=left] will hold a simple state consisting of two values (one a 32-bit[/align]
[align=left] integer, the other a double).[/align]
[align=left] [/align]
[align=left] The interface name is "org.maemo.Value".[/align]
[align=left] One known reference implementation is provided for it by the[/align]
[align=left] "/GlobalValue" object found via a well-known name of[/align]
[align=left] "org.maemo.Platdev_ex". -->[/align]
[align=left] [/align]
[align=left]<node>[/align]
[align=left] <interface name="org.maemo.Value">[/align]
[align=left] [/align]
[align=left] <!-- Method definitions -->[/align]
[align=left] [/align]
[align=left] <!-- getvalue1(): returns the first value (int) -->[/align]
[align=left] <method name="getvalue1">[/align]
[align=left] <!-- NOTE Naming arguments is not mandatory, but is recommended[/align]
[align=left] so that D-Bus introspection tools are more useful.[/align]
[align=left] Otherwise the arguments will be automatically named[/align]
[align=left] "arg0", "arg1" and so on. -->[/align]
[align=left] <arg type="i" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- getvalue2(): returns the second value (double) -->[/align]
[align=left] <method name="getvalue2">[/align]
[align=left] <arg type="d" name="cur_value" direction="out"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- setvalue1(int newValue): sets value1 -->[/align]
[align=left] <method name="setvalue1">[/align]
[align=left] <arg type="i" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] <!-- setvalue2(double newValue): sets value2 -->[/align]
[align=left] <method name="setvalue2">[/align]
[align=left] <arg type="d" name="new_value" direction="in"/>[/align]
[align=left] </method>[/align]
[align=left] [/align]
[align=left] </interface>[/align]
[align=left]</node>[/align]
[align=left] [/align]
[align=left]写完XML文件后,下面我们是不是可以使用D-Bus工具来生成代码了?别急!我们还要对XML文件做些自动检查。主要检查两个方面:是否符合XML1.0规范;验证XML结构(即:元素是否成对匹配)。结构验证的规则是由DTD(Document Type Definition)文档规定的。D-Bus规定的XML格式如下:[/align]
[align=left]<!-- DTD for D-BUS Introspection data -->[/align] [align=left]<!-- (C) 2005-02-02 David A. Wheeler; released under the D-BUS licenses,[/align] [align=left] GNU GPL version 2 (or greater) and AFL 1.1 (or greater) -->[/align] [align=left] [/align] [align=left]<!-- see D-BUS specification for documentation -->[/align] [align=left] [/align] [align=left]<!ELEMENT node (interface*,node*)>[/align] [align=left]<!ATTLIST node name CDATA #REQUIRED>[/align] [align=left] [/align] [align=left]<!ELEMENT interface (annotation*,method*,signal*,property*)>[/align] [align=left]<!ATTLIST interface name CDATA #REQUIRED>[/align] [align=left] [/align] [align=left]<!ELEMENT method (annotation*,arg*)>[/align] [align=left]<!ATTLIST method name CDATA #REQUIRED>[/align] [align=left] [/align] [align=left]<!ELEMENT arg EMPTY>[/align] [align=left]<!ATTLIST arg name CDATA #IMPLIED>[/align] [align=left]<!ATTLIST arg type CDATA #REQUIRED>[/align] [align=left]<!-- Method arguments SHOULD include "direction",[/align] [align=left] while signal and error arguments SHOULD not (since there's no point).[/align] [align=left] The DTD format can't express that subtlety. -->[/align] [align=left]<!ATTLIST arg direction (in|out) "in">[/align] [align=left] [/align] [align=left]<!ELEMENT signal (arg,annotation)>[/align] [align=left]<!ATTLIST signal name CDATA #REQUIRED>[/align] [align=left] [/align] [align=left]<!ELEMENT property (annotation)> <!-- AKA "attribute" -->[/align] [align=left]<!ATTLIST property name CDATA #REQUIRED>[/align] [align=left]<!ATTLIST property type CDATA #REQUIRED>[/align] [align=left]<!ATTLIST property access (read|write|readwrite) #REQUIRED>[/align] [align=left] [/align] [align=left]<!ELEMENT annotation EMPTY> <!-- Generic metadata -->[/align] [align=left]<!ATTLIST annotation name CDATA #REQUIRED>[/align] [align=left]<!ATTLIST annotation value CDATA #REQUIRED>[/align] [align=left] [/align] |
[align=left]仅仅检查了DTD有效性,还不完美:因为DTD类型检查只是对语法做检查,并不能对语义/含义做检查。[/align]
[align=left]为了检查语义,我们使用另外一个工具:checkxml, 把它写到你的makefile中:[/align]
[align=left]# One extra target (which requires xmllint, from package libxml2-utils)[/align]
[align=left]# is available to verify the well-formedness and the structure of the[/align]
[align=left]# interface definition xml file.[/align]
[align=left]#[/align]
[align=left]# Use the 'checkxml' target to run the interface XML through xmllint[/align]
[align=left]# verification. You'll need to be connected to the Internet in order[/align]
[align=left]# for xmllint to retrieve the DTD from fd.o (unless you setup local[/align]
[align=left]# catalogs, which are not covered here).[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# Interface XML name (used in multiple targets)[/align]
[align=left]interface_xml := value-dbus-interface.xml[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# Special target to run DTD validation on the interface XML. Not run[/align]
[align=left]# automatically (since xmllint isn't always available and also needs[/align]
[align=left]# Internet connectivity).[/align]
[align=left]checkxml: $(interface_xml)[/align]
[align=left] @xmllint --valid --noout $<[/align]
[align=left] @echo $< checks out ok[/align]
[align=left]检查语义:[/align]
[align=left] [/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make checkxml[/align]
[align=left]value-dbus-interface.xml checks out ok[/align]
[align=left] [/align]
[align=left]为了验证DTD/checkxml能否起到作用,我们简单修改一下上面的XML文件,增加一个DTD中没有规定的节点</invalidElement> ,并且去掉一个method开始标签,看看效果:[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make checkxml[/align]
[align=left]value-dbus-interface.xml:36: element invalidElement: validity error :[/align]
[align=left] No declaration for element invalidElement[/align]
[align=left] </invalidElement>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:53: parser error :[/align]
[align=left] Opening and ending tag mismatch: method line 39 and interface[/align]
[align=left] </interface>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:54: parser error :[/align]
[align=left] Opening and ending tag mismatch: interface line 22 and node[/align]
[align=left]</node>[/align]
[align=left] ^[/align]
[align=left]value-dbus-interface.xml:55: parser error :[/align]
[align=left] Premature end of data in tag node line 21[/align]
[align=left] [/align]
[align=left]^[/align]
[align=left]make: *** [checkxml] Error 1[/align]
[align=left]上面,第一个被发现的错误(验证错误),是由于XML文件不符合DTD的规定导致的。第二个(解析错误)则是没有正确的书写XML。[/align]
[align=left]完成XML文件的两方面的检查后,我们开始生成代码。这个生成的代码我们叫它“glue”代码,什么意思呢?glue的本意是粘合,就是通过glue代码完成从Glib到D-Bus的映射关系:[/align]
[align=left]图:[/align]
[align=left][/align]
[align=left]由XML文件,运行dbus-binding-tool命令,辅以不同的参数,可以生成client侧和sever侧的stub代码,生成的client stub和server stub代码正好构成了Client/Server结构。由这两个stub来完成Glib与D-Bus的互通,由于应用程序使用Glib,进而完成应用程序与D-Bus的互通。我们把makefile改写一下:[/align]
[align=left]# Define a list of generated files so that they can be cleaned as well[/align]
[align=left]cleanfiles := value-client-stub.h /[/align]
[align=left] value-server-stub.h[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]# If the interface XML changes, the respective stub interfaces will be[/align]
[align=left]# automatically regenerated. Normally this would also mean that your[/align]
[align=left]# builds would fail after this since you'd be missing implementation[/align]
[align=left]# code.[/align]
[align=left]# 这里的—prefix前缀会反映到你的stub代码中,会在生成的结构变量和函数名称中添加这个前缀,这个前缀大家不要省略,因为这里仅仅是生成头文件,实际的实现代码需要我们自己写,但是我们需要拷贝这里的声明到.c文件中,如果不指定这个前缀,并且有多个模块的话,会冲突的;[/align]
[align=left] [/align]
[align=left]# mode指定为glib-server就生成server stub代码;指定为glib-client[/align]
[align=left]# 则生成client stub代码[/align]
[align=left] [/align]
[align=left]# 生成sever stub代码[/align]
[align=left]value-server-stub.h: $(interface_xml)[/align]
[align=left] dbus-binding-tool --prefix=value_object --mode=glib-server /[/align]
[align=left] $< > $@[/align]
[align=left] [/align]
[align=left]# 生成client stub代码[/align]
[align=left]value-client-stub.h: $(interface_xml)[/align]
[align=left] dbus-binding-tool --prefix=value_object --mode=glib-client /[/align]
[align=left] $< > $@[/align]
[align=left] [/align]
[align=left]# ... Listing cut for brevity ...[/align]
[align=left] [/align]
[align=left]clean:[/align]
[align=left] $(RM) $(targets) $(cleanfiles) *.o[/align]
[align=left]这里生成的client侧的代码是想通过Glib去访问对象的实现,而server侧代码则是实现对象。[/align]
[align=left]生成两个头文件:[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > make value-server-stub.h value-client-stub.h[/align]
[align=left]dbus-binding-tool --prefix=value_object --mode=glib-server /[/align]
[align=left] value-dbus-interface.xml > value-server-stub.h[/align]
[align=left]dbus-binding-tool --prefix=value_object --mode=glib-client /[/align]
[align=left] value-dbus-interface.xml > value-client-stub.h[/align]
[align=left][sbox-CHINOOK_X86: ~/glib-dbus-sync] > ls -la value*stub.h[/align]
[align=left]-rw-rw-r-- 1 user user 5184 Nov 21 14:02 value-client-stub.h[/align]
[align=left]-rw-rw-r-- 1 user user 10603 Nov 21 14:02 value-server-stub.h[/align]
[align=left]在开始实现实际的对象之前,我们先来瞄一眼生成的代码,看看到底什么妖蛾子。先看看server侧的stub文件:[/align]
[align=left]/* Generated by dbus-binding-tool; do not edit! */[/align]
[align=left] [/align]
[align=left] /*... Listing cut for brevity ...*/[/align]
[align=left] [/align]
[align=left]#include <dbus/dbus-glib.h>[/align]
[align=left]static const DBusGMethodInfo dbus_glib_value_object_methods[] = {[/align]
[align=left] { (GCallback) value_object_getvalue1,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER, 0 },[/align]
[align=left] { (GCallback) value_object_getvalue2,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__POINTER_POINTER, 47 },[/align]
[align=left] { (GCallback) value_object_setvalue1,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__INT_POINTER, 94 },[/align]
[align=left] { (GCallback) value_object_setvalue2,[/align]
[align=left] dbus_glib_marshal_value_object_BOOLEAN__DOUBLE_POINTER, 137 },[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]const DBusGObjectInfo dbus_glib_value_object_object_info = {[/align]
[align=left] 0,[/align]
[align=left] dbus_glib_value_object_methods,[/align]
[align=left] 4,[/align]
[align=left]"org.maemo.Value/0getvalue1/0S/0cur_value/0O/0F/0N/0i/0/0"[/align]
[align=left]"org.maemo.Value/0getvalue2/0S/0cur_value/0O/0F/0N/0d/0/0"[/align]
[align=left]"org.maemo.Value/0setvalue1/0S/0new_value/0I/0i/0/0"[/align]
[align=left]"org.maemo.Value/0setvalue2/0S/0new_value/0I/0d/0/0/0",[/align]
[align=left]"/0",[/align]
[align=left]"/0"[/align]
[align=left]};[/align]
[align=left][ The method table and object introspection data (glib-dbus-sync/value-server-stub.h) ][/align]
[align=left]在这个头文件中,生成的4个函数:value_object_getvalue1, value_object_getvalue2, value_object_setvalue1value_object_setvalue2. 需要我们去实现;在函数表dbus_glib_value_object_methods,由函数地址;marshalling函数(用于在Glib和D-Bus之间marshal数据)组成。[/align]
[align=left]Marshaling 的作用就是参数从一种形式转换为另外一种形式,主要是为了两种不同参数能互相兼容地传递。Marshalling技术是在大多数RPC中运用的。由于Glib和D-Bus各自有不同的类型系统,如果我们手动去写这种转换代码,是很乏味和无聊的。Binding tool可以帮忙解决这个问题。[/align]
[align=left]上述头文件中还有一个比较有意思的东西:xxx_object_info结构变量,这个结构变量是要注册给D-Bus daemon的,并且把它发布在通道(bus)中(然后client侧才能发现并调用其函数),这个过程其实和我们的生活很贴近,比如公交车上的电视广告:厂家把商品(看作object)的广告,送到广告发布商那里,广告商把该广告投放到公交车(看作D-Bus daemon)的电视中, 消费者(看作client)看到了该广告,可能购买这个产品(调用object的函数)。上面很长的字符串将会安装interface规范的样式进行压缩编码,对于函数名、参数等也是按照这样编码的。在这个头文件中还有很多自省的代码,这也是非常重要的部分,最后再解释。[/align]
[align=left]正如头文件的第一句提示:这是dbus-binding-tool自动生成的代码,请不要编辑它。因为每次编译都回自动生成这个文件,你如果修改了这里的头文件,会被覆盖掉的。所以最好不要修改。那如何使用这里的代码呢,你只要拷贝粘帖这里的代码到.c文件中就行了。[/align]
[align=left]下面我们就继续:实现在server stub函数表中调用的具体函数。We'll next continue with the server implementation for the functions that are called via the method table.[/align]
[align=left] [/align]
[align=left]定义一个对象,这个过程就是GObject的八股文:[/align]
[align=left]/* This defines the per-instance state.[/align]
[align=left] [/align]
[align=left] Each GObject must start with the 'parent' definition so that common[/align]
[align=left] operations that all GObjects support can be called on it. */[/align]
[align=left]typedef struct {[/align]
[align=left] /* The parent class object state. */[/align]
[align=left] GObject parent; //要继承自GObject,这样才能用GObject的便利[/align]
[align=left] /* Our first per-object state variable. */[/align]
[align=left] gint value1;[/align]
[align=left] /* Our second per-object state variable. */[/align]
[align=left] gdouble value2;[/align]
[align=left]} ValueObject;[/align]
[align=left] [/align]
[align=left]/* Per class state.[/align]
[align=left] [/align]
[align=left] For the first Value implementation we only have the bare minimum,[/align]
[align=left] that is, the common implementation for any GObject class. */[/align]
[align=left]typedef struct {[/align]
[align=left] /* The parent class state. */[/align]
[align=left] GObjectClass parent;[/align]
[align=left]} ValueObjectClass;[/align]
[align=left]下面接着定义一些宏,全是用G_TYPE_开头的,你不必知道的太多,会套用就行,如果想知道的很详细,可以参考:http://maemo.org/api_refs/4.0/gobject/index.html.[/align]
[align=left]/* Forward declaration of the function that will return the GType of[/align]
[align=left] the Value implementation. Not used in this program since we only[/align]
[align=left] need to push this over the D-Bus. */[/align]
[align=left]GType value_object_get_type(void);[/align]
[align=left] [/align]
[align=left]/* Macro for the above. It is common to define macros using the[/align]
[align=left] naming convention (seen below) for all GType implementations,[/align]
[align=left] and that's why we're going to do that here as well. */[/align]
[align=left] [/align]
[align=left]/* 下面这些定义其实就是“八股文”,固定的格式,你只要套用就行,等熟悉了后,你也不会一个个写,而是拷贝模板。[/align]
[align=left]*/[/align]
[align=left]#define VALUE_TYPE_OBJECT (value_object_get_type())[/align]
[align=left] [/align]
[align=left]#define VALUE_OBJECT(object) /[/align]
[align=left] (G_TYPE_CHECK_INSTANCE_CAST((object), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObject))[/align]
[align=left]#define VALUE_OBJECT_CLASS(klass) /[/align]
[align=left] (G_TYPE_CHECK_CLASS_CAST((klass), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObjectClass))[/align]
[align=left]#define VALUE_IS_OBJECT(object) /[/align]
[align=left] (G_TYPE_CHECK_INSTANCE_TYPE((object), /[/align]
[align=left] VALUE_TYPE_OBJECT))[/align]
[align=left]#define VALUE_IS_OBJECT_CLASS(klass) /[/align]
[align=left] (G_TYPE_CHECK_CLASS_TYPE((klass), /[/align]
[align=left] VALUE_TYPE_OBJECT))[/align]
[align=left]#define VALUE_OBJECT_GET_CLASS(obj) /[/align]
[align=left] (G_TYPE_INSTANCE_GET_CLASS((obj), /[/align]
[align=left] VALUE_TYPE_OBJECT, ValueObjectClass))[/align]
[align=left] [/align]
[align=left]/* Utility macro to define the value_object GType structure. */[/align]
[align=left]G_DEFINE_TYPE(ValueObject, value_object, G_TYPE_OBJECT)[/align]
[align=left]宏定义完成后,下面我们就着手实现实例初始化和类初始化,类的初始化中我们调用了D-Bus的接口函数,把我们的对象信息注册给了D-Bus了,就是暴露在通道(bus)中了,这样,客户端就能找到这个对象了。[/align]
[align=left]/**[/align]
[align=left] * Since the stub generator will reference the functions from a call[/align]
[align=left] * table, the functions must be declared before the stub is included.[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue1(ValueObject* obj, gint* value_out,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_getvalue2(ValueObject* obj, gdouble* value_out,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_setvalue1(ValueObject* obj, gint value_in,[/align]
[align=left] GError** error);[/align]
[align=left]gboolean value_object_setvalue2(ValueObject* obj, gdouble value_in,[/align]
[align=left] GError** error);[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Pull in the stub for the server side.[/align]
[align=left] */[/align]
[align=left]#include "value-server-stub.h"[/align]
[align=left] [/align]
[align=left] /*... Listing cut for brevity ...*/[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Per object initializer[/align]
[align=left] *[/align]
[align=left] * Only sets up internal state (both values set to zero)[/align]
[align=left] */[/align]
[align=left]static void value_object_init(ValueObject* obj) {[/align]
[align=left] dbg("Called");[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] obj->value1 = 0;[/align]
[align=left] obj->value2 = 0.0;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Per class initializer[/align]
[align=left] *[/align]
[align=left] * Registers the type into the GLib/D-Bus wrapper so that it may add[/align]
[align=left] * its own magic.[/align]
[align=left] */[/align]
[align=left]static void value_object_class_init(ValueObjectClass* klass) {[/align]
[align=left] [/align]
[align=left] dbg("Called");[/align]
[align=left] [/align]
[align=left] g_assert(klass != NULL);[/align]
[align=left] [/align]
[align=left] dbg("Binding to GLib/D-Bus");[/align]
[align=left] [/align]
[align=left] /* Time to bind this GType into the GLib/D-Bus wrappers.[/align]
[align=left] NOTE: This is not yet "publishing" the object on the D-Bus, but[/align]
[align=left] since it is only allowed to do this once per class[/align]
[align=left] creation, the safest place to put it is in the class[/align]
[align=left] initializer.[/align]
[align=left] Specifically, this function adds "method introspection[/align]
[align=left] data" to the class so that methods can be called over[/align]
[align=left] the D-Bus. */[/align]
[align=left] dbus_g_object_type_install_info(VALUE_TYPE_OBJECT,[/align]
[align=left] &dbus_glib_value_object_object_info);[/align]
[align=left] [/align]
[align=left] dbg("Done");[/align]
[align=left] /* All done. Class is ready to be used for instantiating objects */[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]下面我们实现具体的get /set函数,也就是object提供的methods。请注意:函数名和参数都要和stub头文件中的一模一样:[/align]
[align=left]/**[/align]
[align=left] * Function that gets called when someone tries to execute "setvalue1"[/align]
[align=left] * over the D-Bus. (Actually the marshaling code from the stubs gets[/align]
[align=left] * executed first, but they will eventually execute this function.)[/align]
[align=left] *[/align]
[align=left] * NOTE: If you change the name of this function, the generated[/align]
[align=left] * stubs will no longer find it! On the other hand, if you[/align]
[align=left] * decide to modify the interface XML, this is one of the places[/align]
[align=left] * that you'll have to modify as well.[/align]
[align=left] * This applies to the next four functions (including this one).[/align]
[align=left] */[/align]
[align=left]gboolean value_object_setvalue1(ValueObject* obj, gint valueIn,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (valueIn=%d)", valueIn);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] /* Change the value. */[/align]
[align=left] obj->value1 = valueIn;[/align]
[align=left] [/align]
[align=left] /* Return success to GLib/D-Bus wrappers. In this case we don't need[/align]
[align=left] to touch the supplied error pointer-pointer. */[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "setvalue2".[/align]
[align=left] * Other than this function operating with different type input[/align]
[align=left] * parameter (and different internal value), all the comments from[/align]
[align=left] * set_value1 apply here as well.[/align]
[align=left] */[/align]
[align=left]gboolean value_object_setvalue2(ValueObject* obj, gdouble valueIn,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (valueIn=%.3f)", valueIn);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] obj->value2 = valueIn;[/align]
[align=left] [/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "getvalue1".[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue1(ValueObject* obj, gint* valueOut,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (internal value1 is %d)", obj->value1);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] [/align]
[align=left] /* Check that the target pointer is not NULL.[/align]
[align=left] Even is the only caller for this will be the GLib-wrapper code,[/align]
[align=left] we cannot trust the stub generated code and should handle the[/align]
[align=left] situation. We will terminate with an error in this case.[/align]
[align=left] [/align]
[align=left] Another option would be to create a new GError, and store[/align]
[align=left] the error condition there. */[/align]
[align=left] g_assert(valueOut != NULL);[/align]
[align=left] [/align]
[align=left] /* Copy the current first value to caller specified memory. */[/align]
[align=left] *valueOut = obj->value1;[/align]
[align=left] [/align]
[align=left] /* Return success. */[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
[align=left] [/align]
[align=left]/**[/align]
[align=left] * Function that gets executed on "getvalue2".[/align]
[align=left] * (Again, similar to "getvalue1").[/align]
[align=left] */[/align]
[align=left]gboolean value_object_getvalue2(ValueObject* obj, gdouble* valueOut,[/align]
[align=left] GError** error) {[/align]
[align=left] [/align]
[align=left] dbg("Called (internal value2 is %.3f)", obj->value2);[/align]
[align=left] [/align]
[align=left] g_assert(obj != NULL);[/align]
[align=left] g_assert(valueOut != NULL);[/align]
[align=left] [/align]
[align=left] *valueOut = obj->value2;[/align]
[align=left] return TRUE;[/align]
[align=left]}[/align]
相关文章推荐
- Maemo Linux手机平台系列分析:10 Maemo平台开发之 使用Glib/D-Bus做异步操作
- Maemo Linux手机平台系列分析:(14) Maemo平台开发之 设计D-Bus server时要注意的若干问题
- Maemo Linux手机平台系列分析:6 Maemo平台开发之D-Bus
- Maemo Linux手机平台系列分析:9 Maemo平台开发之 使用D-Bus信号做异步操作
- Maemo Linux手机平台系列分析:(17) Maemo应用开发: GNU make 与makefile
- Maemo Linux手机平台系列分析:(15) Maemo应用开发
- Maemo Linux手机平台系列分析:11 Maemo平台开发之 异步GConf
- Maemo Linux手机平台分析系列:3 安装Maemo平台宿主Linux系统
- Maemo Linux手机平台系列分析:2 术语
- Maemo Linux手机平台系列分析:12 分析其Platform后,再回头整体看看Maemo
- Maemo Linux手机平台系列分析 scratchboxp安装
- Maemo Linux手机平台系列分析:1 技术概览
- Maemo Linux手机平台系列分析:7 Maemo平台开发之LibOSSO
- SAP企业移动平台开发探索系列5 – 使用SUP开发WindowsMobile离线应用
- Linux平台,使用JavaComm3 API及SMSLib项目实现在Web Application中发送手机短信的功能
- Linux (x86) Exploit 开发系列教程之十二 释放后使用
- 【转】Maemo平台开发之D-Bus
- 自娱自乐9之Linux DMA使用1(三星平台DMA分析)
- 使用Vagrant在Windows平台搭建嵌入式Linux开发环境(3)nfs服务器搭建
- 利用IDEA进行JNI开发:使用NDK生成Linux平台下的so文件