|
|
|
/* qdbusobject.h DBUS service object interface
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2007 Kevin Krammer <kevin.krammer@gmx.at>
|
|
|
|
*
|
|
|
|
* Licensed under the Academic Free License version 2.1
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
|
* USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef TQDBUSOBJECT_H
|
|
|
|
#define TQDBUSOBJECT_H
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @page dbusservice Providing services over D-Bus
|
|
|
|
*
|
|
|
|
* Contents:
|
|
|
|
* - @ref dbusservice-introduction
|
|
|
|
* - @ref dbusservice-example
|
|
|
|
* - @ref dbusservice-requestname
|
|
|
|
* - @ref dbusservice-registerobjects
|
|
|
|
* - @ref dbusservice-interfaces
|
|
|
|
*
|
|
|
|
* @section dbusservice-introduction Introduction
|
|
|
|
*
|
|
|
|
* The TQt3 bindings do not support autogeneration of service objects yet. In
|
|
|
|
* order to provide interfaces over D-Bus, an application has to implement the
|
|
|
|
* TQT_DBusObjectBase interface and register an instance of the resulting class
|
|
|
|
* with the TQT_DBusConnection.
|
|
|
|
*
|
|
|
|
* @section dbusservice-example A simple D-Bus client example
|
|
|
|
*
|
|
|
|
* @code
|
|
|
|
* #include <dbus/tqdbusconnection.h>;
|
|
|
|
* #include <dbus/tqdbusobject.h>;
|
|
|
|
*
|
|
|
|
* class TQStringList;
|
|
|
|
*
|
|
|
|
* class TestService : public TQT_DBusObjectBase
|
|
|
|
* {
|
|
|
|
* public:
|
|
|
|
* TestService(const TQT_DBusConnection& connection);
|
|
|
|
* virtual ~TestService();
|
|
|
|
*
|
|
|
|
* protected:
|
|
|
|
* virtual bool handleMethodCall(const TQT_DBusMessage& message);
|
|
|
|
*
|
|
|
|
* private:
|
|
|
|
* TQT_DBusConnection m_connection;
|
|
|
|
*
|
|
|
|
* private:
|
|
|
|
* TQStringList sortStrings(const TQStringList& list);
|
|
|
|
* };
|
|
|
|
* @endcode
|
|
|
|
* @code
|
|
|
|
*
|
|
|
|
* #include <tqstringlist.h>;
|
|
|
|
*
|
|
|
|
* #include <dbus/tqdbuserror.h>;
|
|
|
|
* #include <dbus/tqdbusmessage.h>;
|
|
|
|
*
|
|
|
|
* TestService::TestService(const TQT_DBusConnection& connection) : m_connection(connection)
|
|
|
|
* {
|
|
|
|
* m_connection.registerObject("/ListSorter", this);
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* TestService::~TestService()
|
|
|
|
* {
|
|
|
|
* m_connection.unregisterObject("/ListSorter");
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* // return false to let D-Bus send a standard error message that the method is unknown
|
|
|
|
*
|
|
|
|
* bool TestService::handleMethod(const TQT_DBusMessage& message)
|
|
|
|
* {
|
|
|
|
* if (message.interface() != "org.example.Sort") return false;
|
|
|
|
*
|
|
|
|
* if (message.member() == "Strings")
|
|
|
|
* {
|
|
|
|
* // check parameters
|
|
|
|
*
|
|
|
|
* if (message.count() != 1 || message[0].type() != TQT_DBusData::List)
|
|
|
|
* {
|
|
|
|
* // method signature not what we expected
|
|
|
|
*
|
|
|
|
* TQT_DBusError error = TQT_DBusError::stdInvalidArgs(
|
|
|
|
* "Expected one argument of type array of string");
|
|
|
|
*
|
|
|
|
* TQT_DBusMessage reply = TQT_DBusMessage::methodError(message, error);
|
|
|
|
*
|
|
|
|
* // send error
|
|
|
|
*
|
|
|
|
* m_connection.send(reply);
|
|
|
|
*
|
|
|
|
* // tell D-Bus we did handle the call
|
|
|
|
*
|
|
|
|
* return true;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* // call implementation
|
|
|
|
*
|
|
|
|
* TQStringList result = sortStrings(message[0].toTQStringList());
|
|
|
|
*
|
|
|
|
* // prepare reply
|
|
|
|
*
|
|
|
|
* TQT_DBusMessage reply = TQT_DBusMessage::methodReply(message);
|
|
|
|
*
|
|
|
|
* reply << TQT_DBusData::fromList(result);
|
|
|
|
*
|
|
|
|
* // send reply
|
|
|
|
*
|
|
|
|
* m_connection.send(reply);
|
|
|
|
*
|
|
|
|
* // tell D-Bus we did handle the call
|
|
|
|
*
|
|
|
|
* return true;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* return false;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* TQStringList TestService::sortStrings(const TQStringList& list)
|
|
|
|
* {
|
|
|
|
* TQStringList result = list;
|
|
|
|
*
|
|
|
|
* result.sort();
|
|
|
|
*
|
|
|
|
* return result;
|
|
|
|
* }
|
|
|
|
* @endcode
|
|
|
|
* @code
|
|
|
|
* int main(int argc, char** argv)
|
|
|
|
* {
|
|
|
|
* TQApplication app(argc, argv, false);
|
|
|
|
*
|
|
|
|
* TQT_DBusConnection connection = TQT_DBusConnection::sessionBus();
|
|
|
|
* if (!connection.isConnected())
|
|
|
|
* tqFatal("Cannot connect to session bus");
|
|
|
|
*
|
|
|
|
* // try to get a specific service name
|
|
|
|
* if (!connection.requestName("org.example.SortService"))
|
|
|
|
* {
|
|
|
|
* tqWarning("Requesting name 'org.example.SortService' failed. "
|
|
|
|
* "Will only be addressable through unique name '%s'",
|
|
|
|
* connection.uniqueName().local8Bit().data());
|
|
|
|
* }
|
|
|
|
* else
|
|
|
|
* {
|
|
|
|
* tqDebug("Requesting name 'org.example.SortService' successfull");
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* TestService service(connection);
|
|
|
|
*
|
|
|
|
* return app.exec();
|
|
|
|
* }
|
|
|
|
* @endcode
|
|
|
|
*
|
|
|
|
* @section dbusservice-requestname Requesting service name
|
|
|
|
*
|
|
|
|
* When an application connects to D-Bus it gets a unique name generated by
|
|
|
|
* the bus daemon.
|
|
|
|
*
|
|
|
|
* However, an application providing service will often want to be reachable
|
|
|
|
* under a fixed name, like a webserver being reachable through a domain name
|
|
|
|
* independent from its actual IP address.
|
|
|
|
* See section @ref dbusconventions-servicename for details on service names.
|
|
|
|
*
|
|
|
|
* In order to get such a specific name an application has to request it
|
|
|
|
* using TQT_DBusConnection::requestName()
|
|
|
|
*
|
|
|
|
* The example above request @c "org.example.SortService" but continues with
|
|
|
|
* the default unique name in the case some other application is currently
|
|
|
|
* owning that name.
|
|
|
|
*
|
|
|
|
* @section dbusservice-registerobjects Registering objects
|
|
|
|
*
|
|
|
|
* To make service objects available to other applications on the same
|
|
|
|
* bus the application has to register the objects instances with the
|
|
|
|
* connection to the bus using TQT_DBusConnection::registerObject()
|
|
|
|
*
|
|
|
|
* Registering means to specify an object path where the object will be
|
|
|
|
* located, i.e. how it can be unambiguously be addressed in method calls.
|
|
|
|
* See section @ref dbusconventions-objectpath for details on object paths.
|
|
|
|
*
|
|
|
|
* If the applications has introspectable objects it is recommended to
|
|
|
|
* register an introspectable root object, i.e. using @c "/" as the path, so
|
|
|
|
* other applications have a common place to start asking for introspection
|
|
|
|
* data.
|
|
|
|
*
|
|
|
|
* In the example above a service object providing sorting services on lists is
|
|
|
|
* registered on the path @c "/ListSorter"
|
|
|
|
*
|
|
|
|
* @section dbusservice-interfaces Service interfaces
|
|
|
|
*
|
|
|
|
* D-Bus methods and signals of a service object a grouped into interfaces.
|
|
|
|
*
|
|
|
|
* See section @ref dbusconventions-interfacename for details on interface
|
|
|
|
* naming.
|
|
|
|
*
|
|
|
|
* An object can implement any number of interfaces, for example the interface
|
|
|
|
* for the functionality it wants to provide and a D-Bus standard interface like
|
|
|
|
* @c "org.freedesktop.DBus.Introspectable" for providing an XML description of
|
|
|
|
* all its interfaces.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The service object of the example above implements just one interface
|
|
|
|
* @c "org.example.Sort" and its handleMethodCall() explicitly checks all
|
|
|
|
* received messages and rejects any messsage not sent to this particular
|
|
|
|
* interface by returning @c false and thus telling the D-Bus layer to
|
|
|
|
* generate a standard error response.
|
|
|
|
*
|
|
|
|
* Multiple interfaces can of course be directly implemented in one C++ class,
|
|
|
|
* however it might sometimes be wise to delegate calls for different
|
|
|
|
* interfaces to different implementations:
|
|
|
|
* @code
|
|
|
|
* class Interface1 : public TQT_DBusObjectBase
|
|
|
|
* {
|
|
|
|
* public:
|
|
|
|
* Interface1(const TQT_DBusConnection&);
|
|
|
|
*
|
|
|
|
* protected:
|
|
|
|
* virtual bool handleMethodCall(const TQT_DBusMessage&);
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* class Interface2 : public TQT_DBusObjectBase
|
|
|
|
* {
|
|
|
|
* public:
|
|
|
|
* Interface2(const TQT_DBusConnection&);
|
|
|
|
*
|
|
|
|
* protected:
|
|
|
|
* virtual bool handleMethodCall(const TQT_DBusMessage&);
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* class MultiInterfaceService : public TQT_DBusObjectBase
|
|
|
|
* {
|
|
|
|
* public:
|
|
|
|
* MultiInterfaceService(const TQT_DBusConnection&);
|
|
|
|
*
|
|
|
|
* protected:
|
|
|
|
* virtual bool handleMethodCall(const TQT_DBusMessage&);
|
|
|
|
*
|
|
|
|
* private:
|
|
|
|
* TQMap<TQString, TQT_DBusObjectBase*> m_interfaces;
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* MultiInterfaceService::MultiInterfaceService(const TQT_DBusConnection& connection)
|
|
|
|
* {
|
|
|
|
* m_interfaces.insert("org.example.Interface1", new Interface1(connection));
|
|
|
|
* m_interfaces.insert("org.example.Interface2", new Interface2(connection));
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* bool MultiInterfaceService::handleMethodCall(const TQT_DBusMessage& message)
|
|
|
|
* {
|
|
|
|
* // delegate call to its interface handler
|
|
|
|
* TQT_DBusObjectBase* handler = m_interfaces[message.interface()];
|
|
|
|
* if (handler != 0)
|
|
|
|
* return delegateMethodCall->(message, handler);
|
|
|
|
* else
|
|
|
|
* return false; // no such interface
|
|
|
|
* }
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @include example-service.h
|
|
|
|
* @example example-service.cpp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tqdbusmacros.h"
|
|
|
|
|
|
|
|
class TQT_DBusMessage;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Base interface for D-Bus service objects
|
|
|
|
*
|
|
|
|
* In order to register a service object with the TQT_DBusConnection it needs to
|
|
|
|
* implement the interface specified by this class.
|
|
|
|
*
|
|
|
|
* The connection will forward all method calls that have a path equivalent
|
|
|
|
* to the path the service object was registered with to the object's
|
|
|
|
* handleMethodCall() method. See TQT_DBusConnection::registerObject()
|
|
|
|
*
|
|
|
|
* If for some reason, e.g. the call is not meant for this interface, or the
|
|
|
|
* method is unknown, the implementation can just return @c false and the
|
|
|
|
* connection will handle the rest.
|
|
|
|
*
|
|
|
|
* See section @ref dbusservice for documentation on how to use TQT_DBusObjectBase
|
|
|
|
*/
|
|
|
|
class TQDBUS_EXPORT TQT_DBusObjectBase
|
|
|
|
{
|
|
|
|
friend class TQT_DBusConnectionPrivate;
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Destroys the object
|
|
|
|
*/
|
|
|
|
virtual ~TQT_DBusObjectBase() {}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* @brief Method call entry point
|
|
|
|
*
|
|
|
|
* This method has to be implemented to handle method calls sent to the
|
|
|
|
* service object.
|
|
|
|
* An object implementation can handle all its interfaces in one class or
|
|
|
|
* again forward the method call to interface implementators.
|
|
|
|
*
|
|
|
|
* If for some reason, e.g. the call is not meant for this interface, or
|
|
|
|
* the method is unknown, the implementation can just return @c false and
|
|
|
|
* the connection will handle the rest.
|
|
|
|
*
|
|
|
|
* If an error occurs during the method call, e.g. the number of parameters
|
|
|
|
* or their types are not what would be expected, the service object
|
|
|
|
* should reply with a TQT_DBusMessage of type TQT_DBusMessage::ErrorMessage
|
|
|
|
* which in turn should include the D-Bus error describing the problem.
|
|
|
|
* See TQT_DBusConnection::send() for sending reply messages.
|
|
|
|
*
|
|
|
|
* See TQT_DBusMessage::methodError() and TQT_DBusMessage::methodReply() on
|
|
|
|
* how to create suitable reply messages for the given method call.
|
|
|
|
*
|
|
|
|
* @param message the method call to handle
|
|
|
|
*
|
|
|
|
* @return @c true if the message can be handled independent if handling
|
|
|
|
* resulted in an error. In this case implementations should an
|
|
|
|
* error reply. Returns @c false only if interface or method are
|
|
|
|
* unknown
|
|
|
|
*/
|
|
|
|
virtual bool handleMethodCall(const TQT_DBusMessage& message) = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Delegate a method call to another object
|
|
|
|
*
|
|
|
|
* When a service object is built as a collection of separated interface
|
|
|
|
* class instances, i.e. each interface of the object is implemented in
|
|
|
|
* its own TQT_DBusObjectBase subclass and the main object just wanst to pass
|
|
|
|
* on the method calls to the respective interface implementations, it
|
|
|
|
* can do so by calling this base class method.
|
|
|
|
*
|
|
|
|
* Since it is a method of the base class, it can call the otherwise
|
|
|
|
* protected handleMethodCall() of the interface implementor.
|
|
|
|
*
|
|
|
|
* See @ref dbusservice-interfaces for an example.
|
|
|
|
*
|
|
|
|
* @param message the method call to delegate
|
|
|
|
* @param delegate the object which should handle the call instead
|
|
|
|
*
|
|
|
|
* @return @c true if the message can be handled independent if handling
|
|
|
|
* resulted in an error. In this case implementations should an
|
|
|
|
* error reply. Returns @c false only if interface or method are
|
|
|
|
* unknown
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
bool delegateMethodCall(const TQT_DBusMessage& message, TQT_DBusObjectBase* delegate)
|
|
|
|
{
|
|
|
|
return delegate->handleMethodCall(message);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|