您的位置:首页 > 运维架构 > Linux

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=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
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)
[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]<!-- 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]然后根据这个模板对你定义的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]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息