基於GDbus與QDbus的DBUS小練習
QDbus
API:QT 的幫助文件
任務描述:
proxy 獲取 adaptor的資料,修改資料,接收資料修改的訊號並檢視新的值。
檔案結構:
➜ DbusTest git:(master) ✗ tree
.
├── Adaptor
│ ├── Adaptor.pro
│ ├── Makefile
│ ├── OrgExampleDdbusTest.xml
│ ├── main.cpp
│ ├── orgexampleddbustest_adaptor.cpp
│ ├── orgexampleddbustest_adaptor.h
│ ├── testadaptor.cpp
│ └── testadaptor.h
├── DbusTest.pro
├── DbusTest.pro.user
└── Proxy
├── Makefile
├── OrgExampleDdbusTest.xml
├── Proxy.pro
├── main.cpp
├── orgexampleddbustest_interface.cpp
├── orgexampleddbustest_interface.h
├── testproxy.cpp
└── testproxy.h
介面檔案 XML:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/com/examples/qdbus/wtest">
<interface name="org.example.qdbus.wtest">
<signal name="valueChanged">
<arg name="newValue" type="d" direction="out"/>
</signal>
<method name="setValue">
<arg name="newValue" type="d" direction="in"/>
</method>
<method name="getValue" type="d" direction="out"/>
</interface>
</node>
依據XML生成原始碼的工具:qdbusxml2cpp
遠端的函式返回值不能傳遞到client,但我們可以通過訊號來傳遞server的value到client。
執行效果:
➜ Proxy git:(master) ✗ ./Proxy.app/Contents/MacOS/Proxy
original value: 0
onValueChanged, interface get value: 20
➜ Adaptor git:(master) ✗ ./Adaptor.app/Contents/MacOS/Adaptor
has error ? QDBusError("", "")
TestAdaptor::getValue 12
TestAdaptor::setValue 20
之所以能在server傳送signal,讓client作出響應,是因為WtestAdaptor和OrgExampleQdbusWtestInterface在建構函式中均有這樣的設定:
setAutoRelaySignals(true);
這樣的話,可以在parent也就是server程序類中直接emit,程序間的訊號轉發就交給WtestAdaptor處理了。
// Adaptor/orgexampleddbustest_adaptor.h
/*
* Adaptor class for interface org.example.qdbus.wtest
*/
class WtestAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.example.qdbus.wtest")
...
// Proxy/orgexampleddbustest_interface.h
/*
* Proxy class for interface org.example.qdbus.wtest
*/
class OrgExampleQdbusWtestInterface: public QDBusAbstractInterface
{
Q_OBJECT
public:
static inline const char *staticInterfaceName()
{ return "org.example.qdbus.wtest"; }
...
工程地址:https://github.com/theArcticOcean/qtLib/tree/master/DbusTest
2018.6.23更新:上面的XML getValue寫法有誤,所以,不能傳遞到client。修正:
<method name= "getValue">
<arg type="d" name="aResult" direction="out" />
</method>
關於arg type的寫法:double對應d,bool對應b,int對應i,unit對應u ……
GDbus
API:https://developer.gnome.org/gio/stable/gdbus-convenience.html
依據XML生成原始碼的工具:gdbus-codegen
還是使用QtDbus例子中的OrgExampleDdbusTest.xml。
相關命令:gdbus-codegen –generate-c-code=DbusTest_code OrgExampleDdbusTest.xml
然後,相關的檔案DbusTest_code.h和DbusTest_code.c便生成了。
目錄結構 (本工程使用waf工具進行編譯):
➜ DbusTest git:(local) ✗ tree
.
├── Client
│ ├── GDbusClient.c
│ └── wscript
├── DbusTest_code.c
├── DbusTest_code.h
├── OrgExampleDdbusTest.xml
├── Server
│ ├── GDbusServer.c
│ └── wscript
├── waf
└── wscript
DbusTest_code.h的部分內容:
struct _OrgExampleQdbusWtestIface
{
GTypeInterface parent_iface;
gboolean (*handle_get_value) (
OrgExampleQdbusWtest *object,
GDBusMethodInvocation *invocation);
gboolean (*handle_set_value) (
OrgExampleQdbusWtest *object,
GDBusMethodInvocation *invocation,
gdouble arg_newValue);
void (*value_changed) (
OrgExampleQdbusWtest *object,
gdouble arg_newValue);
};
//...
/* D-Bus signal emissions functions: */
void org_example_qdbus_wtest_emit_value_changed (
OrgExampleQdbusWtest *object,
gdouble arg_newValue);
/* D-Bus method calls: */
void org_example_qdbus_wtest_call_set_value (
OrgExampleQdbusWtest *proxy,
gdouble arg_newValue,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean org_example_qdbus_wtest_call_set_value_finish (
OrgExampleQdbusWtest *proxy,
GAsyncResult *res,
GError **error);
gboolean org_example_qdbus_wtest_call_set_value_sync (
OrgExampleQdbusWtest *proxy,
gdouble arg_newValue,
GCancellable *cancellable,
GError **error);
//...
OrgExampleQdbusWtest *org_example_qdbus_wtest_proxy_new_for_bus_sync (
GBusType bus_type,
GDBusProxyFlags flags,
const gchar *name,
const gchar *object_path,
GCancellable *cancellable,
GError **error);
//...
OrgExampleQdbusWtest *org_example_qdbus_wtest_skeleton_new (void);
Client/GDbusClient.c 關鍵內容:
static gboolean onValueChanged
(
OrgExampleQdbusWtest *object,
double newValue,
gpointer user_data
)
{
g_print( "onValueChanged, get value: %lf\n", newValue );
return TRUE;
}
int main( int argc, char *argv[] )
{
//...
proxy = org_example_qdbus_wtest_proxy_new_for_bus_sync(
G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
"org.example.qdbus.wtest",
"/com/examples/qdbus/wtest",
NULL,
&error );
if( NULL == proxy )
{
g_print( "proxy init failed: %s", error->message );
exit( 0 );
}
// connect step
g_signal_connect( proxy, "value-changed", G_CALLBACK( onValueChanged ), NULL );
// set new value.
org_example_qdbus_wtest_call_set_value_sync( proxy, 20, NULL, &error );
g_main_loop_run( loop );
// ...
}
Server/GDbusServer.c 關鍵內容:
// Callback function on_handle_set_value has form
// which is deined in struct _OrgExampleQdbusWtestIface
static gboolean on_handle_set_value
(
OrgExampleQdbusWtest *object,
GDBusMethodInvocation *invocation,
gdouble arg_newValue
)
{
g_print( "on_handle_set_value, set value: %lf\n", arg_newValue );
iPrivate.data = arg_newValue;
org_example_qdbus_wtest_complete_set_value( object, invocation );
org_example_qdbus_wtest_emit_value_changed( object, arg_newValue );
return TRUE;
}
static void GBusAcquired_Callback
(
GDBusConnection *connection,
const gchar *name,
gpointer user_data
)
{
GError *error = NULL;
g_print( "GBusAcquired_Callback, name is %s, user_data: %s\n", name, (char *)user_data );
adaptor = org_example_qdbus_wtest_skeleton_new();
// connect step
g_signal_connect( adaptor, "handle-set-value", G_CALLBACK( on_handle_set_value ), NULL );
g_dbus_interface_skeleton_export( G_DBUS_INTERFACE_SKELETON( adaptor ), connection, "/com/examples/qdbus/wtest", &error );
if( NULL != error )
{
g_print( "Failed to export object: %s\n", error->message );
g_error_free( error );
}
}
//...
int main()
{
GMainLoop* loop = NULL;
guint own_id = g_bus_own_name(
G_BUS_TYPE_SESSION,
"org.example.qdbus.wtest",
G_BUS_NAME_OWNER_FLAGS_NONE,
GBusAcquired_Callback,
GBusNameAcquired_Callback,
GBusNameLost_Callback,
NULL,
NULL
);
loop = g_main_loop_new( NULL, FALSE );
g_main_loop_run( loop );
g_bus_unown_name( own_id );
return 0;
}
其中on_handle_set_value就是服務端的setValue函式,客戶端呼叫org_example_qdbus_wtest_call_set_value_sync觸發此函式的執行,在他設定新的value後也傳送了訊號通知客戶端 org_example_qdbus_wtest_emit_value_changed
不知為啥,本工程在mac上總是連不上dbus,重啟dbus守護程序也報錯:
dbus-daemon --config-file=/usr/local/Cellar/dbus/1.12.8/share/dbus-1/session.conf --print-address
dbus-daemon[34918]: Failed to start message bus: Check-in failed: No such process
然後,我就到Ubuntu上嘗試了一波。可以的。
工程地址:https://github.com/theArcticOcean/CLib/tree/master/DbusTest