TDE DBus Tutorial

From Trinity Desktop Project Wiki
Revision as of 21:36, 22 October 2019 by imported>Deloptes (updated to match the status of dbus-1-tqt)
Jump to navigation Jump to search

Introduction

This is a brief introduction on auto-generated DBus interfaces and proxies and their use in TDE.

I hope it will save many people time and headache. Some more information can be found in the documentation of package dbus-1-tqt.

The examples were moved to dbus-1-tqt-example

All the examples are based on the example in dbus-1-tqt for a service providing method “ListSorter” and for such a client.

Create DBus Interface

To create a DBus interface we need first a definition of the interface. More information on the interface type and signatures can be found in official dbus tutorial

The definition of the interface is a xml file. We take as example a file describing "org.example.Service" that has one method "ListSorter". The generated code will automatically include the Introspection interface and we will see how we can create a service to handle both interfaces.

     <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
     "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
     <node name="/org/example/Service">
       <interface name="org.example.Service">
               <method name="ListSorter">
                       <arg name="input" type="as" direction="in" />
                       <arg name="output" type="as" direction="out" />
               </method>
       </interface>
     </node>

Generate the code

To generate the TQt C++ classes we use dbusxml2qt3

     $ dbusxml2qt3 sortexample.xml
     ClassGenerator: processing interface 'org.example.Service'
     Generating org.freedesktop.DBus.Introspectable on demand

in the second step generate the proxy with unique namespace to fit your application

     $/usr/bin/dbusxml2qt3 sortexample.xml

the result is

     $ ls -1
     dbusbaseNode.cpp
     dbusbaseNode.h
     introspectableInterface.cpp
     introspectableInterface.h
     serviceInterface.cpp
     serviceInterface.h
     serviceNode.cpp
     serviceNode.h
     serviceProxy.cpp
     serviceProxy.h
     sortexample.xml

Implement the Service

Now we can create our own service using the interface use the proxy as shown below.

testservice.h

   #include <tqdbusconnection.h>
   #include <tqdbusobject.h>
   #include <tqmap.h>
   #include "serviceInterface.h"
   #include "serviceNode.h"
   class Interface1 : public org::example::ServiceInterface
   {
   public:
       Interface1(TQT_DBusConnection&);
       virtual ~Interface1();
   protected: // implement methods
       virtual bool ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error);
   protected: // implement sending replies
       virtual void handleMethodReply(const TQT_DBusMessage& reply);
   private:
       TQT_DBusConnection *m_connection;
   };
   class MultiInterfaceService : public org::example::ServiceNode
   {
   public:
       MultiInterfaceService(TQT_DBusConnection&);
       ~MultiInterfaceService();
   protected:
       virtual TQT_DBusObjectBase* createInterface(const TQString&);
   private:
       TQMap<TQString, TQT_DBusObjectBase*> m_interfaces;
       TQT_DBusConnection m_connection;
   };

testservice.cpp

   #include <kdebug.h>
   // TQt includes
   #include <tqdom.h>
   #include <tqstring.h>
   #include <tqstringlist.h>
   #include <tqdbuserror.h>
   #include <tqdbusmessage.h>
   #include <tqdbusdatalist.h>
   #include "testservice.h"
   Interface1::Interface1(TQT_DBusConnection &conn)
       : m_connection(&conn)
   {
       kdDebug() << k_funcinfo << endl;
   }
   Interface1::~Interface1(){
       kdDebug() << k_funcinfo << endl;
   }
   void Interface1::handleMethodReply(const TQT_DBusMessage& reply) {
       kdDebug() << k_funcinfo << endl;
       // do something
       m_connection->send(reply);
   }
   bool Interface1::ListSorter(const TQStringList& input, TQStringList& output, TQT_DBusError& error) {
       kdDebug() << k_funcinfo << endl;
       output = input;
       output.sort();
       return true;
   }
   MultiInterfaceService::MultiInterfaceService(TQT_DBusConnection &connection )
       : org::example::ServiceNode(), m_connection(connection)
   {
       kdDebug() << k_funcinfo << endl;
       m_interfaces.insert("org.freedesktop.DBus.Introspectable", this);
       m_interfaces.insert("org.example.Service", new Interface1(m_connection));
       registerObject(m_connection,"/");
   }
   MultiInterfaceService::~MultiInterfaceService(){
       kdDebug() << k_funcinfo << endl;
   }
   TQT_DBusObjectBase* MultiInterfaceService::createInterface(const TQString& interfaceName)
   {
       kdDebug() << k_funcinfo << endl;
       kdDebug() << "Interface Name: " << interfaceName << endl;
       return (TQT_DBusObjectBase*) m_interfaces[interfaceName];
   }

tqdbusexample.cpp

   #include "testservice.h"
   #include <tqapplication.h>
   #include <tqdbusconnection.h>
   int main(int argc, char** argv)
   {
       TQApplication app(argc, argv, false);
       TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
   //  TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
       if (!connection.isConnected())
                       tqFatal("Cannot connect to session bus");
       // try to get a specific service name
       if (!connection.requestName("org.example.Service"))
       {
               tqWarning("Requesting name 'org.example.Service' failed. "
                               "Will only be addressable through unique name '%s'",
               connection.uniqueName().local8Bit().data());
       }
       else
       {
               tqDebug("Requesting name 'org.example.Service' successfull");
       }
       MultiInterfaceService service(connection);
       return app.exec();
   }

buildtqdbusexample.sh

   /usr/bin/g++   -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
   -D_FORTIFY_SOURCE=2  -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
   -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
   -I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
   -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
   -D_REENTRANT -include tqt.h -g  -Wl,-z,relro tqdbusexample.cpp  testservice.cpp \
    serviceNode.cpp introspectableInterface.cpp serviceInterface.cpp \
    -o tqdbusexample -ldbus-1-tqt \
   /opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
   /opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
   /usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui


Using a Proxy

We will use the service from our first example and implement a proxy to utilize the service.

tqdbusproxy.cpp

   #include <tqstringlist.h>
   #include <tqdbusconnection.h>
   #include "serviceProxy.h"
   int main(int argc, char** argv)
   {
       TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
   //  TQT_DBusConnection connection = TQT_DBusConnection::systemBus();
       if (!connection.isConnected())
                       tqFatal("Cannot connect to session bus");
       org::example::ServiceProxy serviceProxy("org.example.Service", "/");
       serviceProxy.setConnection(connection);
       TQStringList list;
       list << "D" << "C" << "B" << "A";
       tqWarning("OUTPUT BEFORE SORT");
       for (TQStringList::iterator it= list.begin(); it != list.end(); ++it) {
               tqWarning("\t%s",(*it).utf8().data());
       }
       TQStringList data;
       TQT_DBusError error;
       if (!serviceProxy.ListSorter(list,data,error)) {
               tqFatal("'org.example.Service.ListSorter' failed. ");
       }
       else {
               tqWarning("OUTPUT AFTER SORT");
               for (TQStringList::iterator it = data.begin(); it != data.end(); ++it) {
                       tqWarning("\t%s",(*it).utf8().data());
               }
       }
       return 0;
   }

buildtqdbusproxy.sh

   tmoc serviceProxy.h -o serviceProxy.moc
   /usr/bin/g++   -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time \
   -D_FORTIFY_SOURCE=2  -DQT_NO_ASCII_CAST -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT \
   -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT -D_REENTRANT -include tqt.h -I/usr/include/tqt3 \
   -I/opt/trinity/include/ -I/usr/include/dbus-1-tqt -I/usr/include/tqt -I -DQT_NO_ASCII_CAST \
   -DQT_CLEAN_NAMESPACE -DQT_NO_STL -DQT_NO_COMPAT -DQT_NO_TRANSLATION -DQT_THREAD_SUPPORT \
   -D_REENTRANT -include tqt.h -g  -Wl,-z,relro tqdbusproxy.cpp serviceProxy.cpp \
    -o tqdbusproxy -ldbus-1-tqt \
   /opt/trinity/lib/libtdeparts.so.2.1.0 /opt/trinity/lib/libtdeio.so.14.0.0 \
   /opt/trinity/lib/libtdecore.so.14.0.0 -ltqt -ltqt-mt -lXrender -lX11 -lc \
   /usr/lib/x86_64-linux-gnu/libz.so -lidn -lXcomposite -lICE -lSM -lutil -lr -lacl -lattr -ltqui


Try it

Start the service

  ./tqdbusexample

Test the service from command line

  $ dbus-send --print-reply  --session --dest=org.example.Service / org.example.Service.ListSorter array:string:"B","A","C"
  method return time=1571779738.342213 sender=:1.91 -> destination=:1.94 serial=7 reply_serial=2
     array [
        string "A"
        string "B"
        string "C"
     ]


Test the service with proxy client

     $ ./tqdbusproxy
     [2019/10/22 23:28:11.982] OUTPUT BEFORE SORT
     [2019/10/22 23:28:11.983]       D
     [2019/10/22 23:28:11.983]       C
     [2019/10/22 23:28:11.983]       B
     [2019/10/22 23:28:11.983]       A
     [2019/10/22 23:28:11.983] OUTPUT AFTER SORT
     [2019/10/22 23:28:11.983]       A
     [2019/10/22 23:28:11.983]       B
     [2019/10/22 23:28:11.983]       C
     [2019/10/22 23:28:11.983]       D


Sample code

Download sample code here