Development/Tutorials/D-Bus/Creating Interfaces: Difference between revisions

    From KDE TechBase
    m (Fix links)
    Line 15: Line 15:


    == Lights: Defining The Interface ==
    == Lights: Defining The Interface ==
    D-Bus interfaces generally reflect the API of one or more classes in the providing application. The interface as seen on the bus can be described using a standard XML format that is described in the [http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format D-Bus specification].
    Such XML might look like this abbreviated example taken from the KWin window manager:
    <code><!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
    "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
    <node>
        <interface name="org.kde.KWin">
        <method name="cascadeDesktop">
        <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
        </method>
        <method name="refresh"/>
        <method name="showWindowMenuAt">
        <arg name="winId" type="x" direction="in"/>
        <arg name="x" type="i" direction="in"/>
        <arg name="y" type="i" direction="in"/>
        </method>
        <method name="setCurrentDesktop">
        <arg name="desktop" type="i" direction="in"/>
        <arg type="b" direction="out"/>
        </method>
      </interface>
    </node></code>
    One can construct this XML by hand, mapping it to the API of a class by hand, but not only is this error prone and time consuming it's not much fun. Fortunately there are ways to automate the process.
    === Defining Methods ===
    === Defining Methods ===
    We will be using the (fictitious) example of an interface that lets the user set the background wallpaper and query the current settings. We will be providing three methods in this interface, which can be seen in the following class definition:
    <code cppqt n>
    #include <QObject>
    class Background : QObject
    {
        Q_OBJECT
        public:
            Background(QObject* parent);
            void doNotExportToDBus();
            void refreshBackground();
            QString currentBackground();
        Q_SIGNALS:
            void doNotExportThisSignal();
            void backgroundChanged();
        public Q_SLOTS:
            bool setBackground(QString name);
        protected Q_SLOTS:
            void dbusCanNotSeeMe();
    };
    </code>
    Next we need to mark which of the above methods we wish to expose via D-Bus. Fortunately, this is quite simple with the following options available to us:
    * export all signals
    * export all public slots
    * export all properties
    * export only scriptable signals
    * export only scriptable public slots
    * export only scriptable properties
    We can also combine the above as we wish. To achieve the desired results in the above example then, we might adjust the class definition thusly:
    <code cppqt>
    #include <QObject>
    class Background : QObject
    {
        Q_OBJECT
        public:
            Background(QObject* parent);
            void doNotExportToDBus();
        Q_SIGNALS:
            void doNotExportThisSignal();
            Q_SCRIPTABLE void backgroundChanged();
        public Q_SLOTS:
            void refreshBackground();
            QString currentBackground();
            bool setBackground(QString name);
        protected Q_SLOTS:
            void dbusCanNotSeeMe();
    };
    </code>
    Note how we moved the methods we wish to export to to be public slots and marked the signal we want to export with <tt>Q_SCRIPTABLE</tt>. We will later then choose to create and interface that exports all the public slots and all scriptable signals.
    We would then go about creating an implementation of this class as defined above.
    {{tip|When exposing an API to other applications via D-Bus other applications and users, via scripting, may come to rely on the calls available in the interface. Changing the D-Bus interface can therefore cause breakage for others. For this reason it is recommended to keep compatibility with publicly advertised D-Bus APIs over the lifespan of a major release of your application.}}
    === Naming The Interface ===
    === Naming The Interface ===
    The next step after having defined our interface is to come up with a name that it will appear as on the bus. These names by convention take on the form of reverse domain names so as to prevent name collisions. Therefore if the domain for your project website is <tt>http://foo.org<tt> you should prefix your interface names with <tt>org.foo</tt>. This is a very common approach to namespacing such exported components.
    Therefore, we may choose to call our interface example <tt>org.foo.Background</tt>. The easiest way to define this is to add a <tt>Q_CLASSINFO</tt> macro entry to our class definition:
    <code cppqt>
    class Background : QObject
    {
        Q_OBJECT
        Q_CLASSINFO("D-Bus Interface", "org.foo.Background")
    </code>
    The interface will now be known as <tt>org.foo.Background</tt>.


    == Camera: Generating the Interface ==
    == Camera: Generating the Interface ==

    Revision as of 22:24, 12 January 2007

    Creating D-Bus Interfaces
    Tutorial Series   D-Bus
    Previous   Introduction
    What's Next   Autostart Services
    Further Reading   n/a

    Abstract

    D-Bus allows applications to expose internal API to the outside world by means of remotely callable interfaces. This tutorial shows how to create and implement such interfaces in your applications.

    Lights: Defining The Interface

    D-Bus interfaces generally reflect the API of one or more classes in the providing application. The interface as seen on the bus can be described using a standard XML format that is described in the D-Bus specification.

    Such XML might look like this abbreviated example taken from the KWin window manager:

    <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node>

       <interface name="org.kde.KWin">
       <method name="cascadeDesktop">
       <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
       </method>
       <method name="refresh"/>
       <method name="showWindowMenuAt">
       <arg name="winId" type="x" direction="in"/>
       <arg name="x" type="i" direction="in"/>
       <arg name="y" type="i" direction="in"/>
       </method>
       <method name="setCurrentDesktop">
       <arg name="desktop" type="i" direction="in"/>
       <arg type="b" direction="out"/>
       </method>
     </interface>
    

    </node>

    One can construct this XML by hand, mapping it to the API of a class by hand, but not only is this error prone and time consuming it's not much fun. Fortunately there are ways to automate the process.

    Defining Methods

    We will be using the (fictitious) example of an interface that lets the user set the background wallpaper and query the current settings. We will be providing three methods in this interface, which can be seen in the following class definition:

    1. include <QObject>

    class Background : QObject {

       Q_OBJECT
    
       public:
           Background(QObject* parent);
    
           void doNotExportToDBus();
    
           void refreshBackground();
           QString currentBackground();
    
       Q_SIGNALS:
           void doNotExportThisSignal();
           void backgroundChanged();
    
       public Q_SLOTS:
           bool setBackground(QString name);
    
       protected Q_SLOTS:
           void dbusCanNotSeeMe();
    

    };

    Next we need to mark which of the above methods we wish to expose via D-Bus. Fortunately, this is quite simple with the following options available to us:

    • export all signals
    • export all public slots
    • export all properties
    • export only scriptable signals
    • export only scriptable public slots
    • export only scriptable properties

    We can also combine the above as we wish. To achieve the desired results in the above example then, we might adjust the class definition thusly:

    1. include <QObject>

    class Background : QObject {

       Q_OBJECT
    
       public:
           Background(QObject* parent);
    
           void doNotExportToDBus();
    
       Q_SIGNALS:
           void doNotExportThisSignal();
           Q_SCRIPTABLE void backgroundChanged();
    
       public Q_SLOTS:
           void refreshBackground();
           QString currentBackground();
           bool setBackground(QString name);
    
       protected Q_SLOTS:
           void dbusCanNotSeeMe();
    

    };

    Note how we moved the methods we wish to export to to be public slots and marked the signal we want to export with Q_SCRIPTABLE. We will later then choose to create and interface that exports all the public slots and all scriptable signals.

    We would then go about creating an implementation of this class as defined above.

    Tip
    When exposing an API to other applications via D-Bus other applications and users, via scripting, may come to rely on the calls available in the interface. Changing the D-Bus interface can therefore cause breakage for others. For this reason it is recommended to keep compatibility with publicly advertised D-Bus APIs over the lifespan of a major release of your application.


    Naming The Interface

    The next step after having defined our interface is to come up with a name that it will appear as on the bus. These names by convention take on the form of reverse domain names so as to prevent name collisions. Therefore if the domain for your project website is http://foo.org you should prefix your interface names with org.foo. This is a very common approach to namespacing such exported components.

    Therefore, we may choose to call our interface example org.foo.Background. The easiest way to define this is to add a Q_CLASSINFO macro entry to our class definition:

    class Background : QObject {

       Q_OBJECT
       Q_CLASSINFO("D-Bus Interface", "org.foo.Background")
    

    The interface will now be known as org.foo.Background.

    Camera: Generating the Interface

    qdbuscpp2xml

    CMake

    Action: Instantiating the Interface At Runtime

    Other Resources