基於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上嘗試了一波。可以的。
server
client
工程地址:https://github.com/theArcticOcean/CLib/tree/master/DbusTest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章