Revision as of 21:36, 11 March 2007

Contents

What are KDE services?

The notion of a service is a central concept in KDE's modular architecture.
There is no strict technical implementation connected with this term - services
can be plugins in the form of shared libraries, or they can be programs
controlled via DCOP. By claiming to be of a certain
service type, a service promises to implement certain APIs or features.
In C++ terms, one can think of a service type as an abstract class, and a service
as an implementation of that interface.

The advantage of this separation is clear: An application utilizing a service
type does not have to know about possible implementations of it. It just uses
the APIs associated with the service type. In this way, the used service can be
changed without affecting the application. Also, the user can configure which
services he prefers for certain features.

Some examples:

The HTML rendering engine used in Konqueror is an embedabble component that implements the service types KParts/ReadOnlyPart and Browser/View.

In KDevelop HEAD, most functionality is packaged in plugins with the service type KDevelop/Part. At startup, all services with this type are loaded, such that you can extend the IDE in a very flexible way.

In the icon view, Konqueror displays - if enabled - thumbnail pictures of images, HTML pages, PDF and text files. This ability can be extended. If you want it to display preview pictures of your own data files with some MIME type, you can implement a service with service type ThumbCreator.

Obviously, a service is not only characterized by the service types it
implements, but also by some properties. For example, a ThumbCreator
does not only claim to implement the C++ class with the ThumbCreator, it
also has a list of MIME types it is responsible for. Similarly, KDevelop
parts have the programming language they support as a property. When
an application requests a service type, it can also list constraints on
the properties of the service. In the above example, when KDevelop loads
the plugins for a Java project, it asks only for the plugins which have
Java as the programming language property. For this purpose, KDE contains
a full-blown CORBA-like trader with a complex query language.

Defining service types

New service types are added by installing a description of them
into the directory KDEDIR/share/servicetypes. In an automake framework,
this can be done with this Makefile.am snippet:

In addition to the usual entries, this example demonstrates how you declare
that a service has some properties. Each property definition corresponds
to a group [PropertyDef::name] in the configuration file. In
this group, the Type entry declares the type of the property.
Possible types are everything that can be stored in a
QVariant.

Defining shared library services

Service definitions are stored in the directory KDEDIR/share/services:

In addition to the usual declarations, an important entry is
X-KDE-Library. This contains the name of the libtool library (without
the .la extension). It also fixes (with the prefix init_
prepended) the name of the exported symbol in the library which returns an
object factory. For the above example, the library must contain the following
function:

extern "C" {

void *init_libkdevdoxygen()
{
return new DoxygenFactory;
}

};

The type of the factory class DoxygenFactory depends on the specific service
type the service implements. In our example of a KDevelop plugin, the factory
must be a KDevFactory (which inherits KLibFactory). More common examples are
KParts::Factory
which is supposed to produce
KParts::ReadOnlyPart
objects or in most cases the generic
KLibFactory.

Using shared library services

In order to use a shared library service in an application, you need to obtain a
KService object
representing it. This is discussed in the
section about MIME types (and in a section about the
trader to be written :-)

With the KService object at hand, you can very simply load the library and
get a pointer to its factory object:

From this point, the further proceeding depends again on the service type. For
generic plugins, you create objects with the method
KLibFactory::create().
For KParts, you must cast the factory pointer to the more specific KParts::Factory and use
its create() method:

Defining DCOP services

A DCOP service is usually implemented as a program that is started up when it is
needed. It then goes into a loop and listens for DCOP connections. The program
may be an interactive one, but it may also run completely or for a part of its
lifetime as a daemon in the background without the user noticing it. An example
for such a daemon is kio_uiserver, which implements user interaction
such as progress dialog for the KIO library. The advantage of such a centralized
daemon in this context is that e.g. the download progress for several different
files can be shown in one window, even if those downloads were initiated from
different applications.

A DCOP service is defined differently from a shared library service. Of course,
it doesn't specify a library, but instead an executable. Also, DCOP services
do not specify a ServiceType line, because usually they are started by their
name. As additional properties, it contains two lines:

X-DCOP-ServiceType specifies the way the service is started. The value
Unique says that the service must not be started more than once. This
means, if you try to start this service (e.g. via
KApplication::startServiceByName(), KDE looks whether it is already
registered with DCOP and uses the running service. If it is not registered yet,
KDE will start it up and wait until is registered. Thus, you can immediately
send DCOP calls to the service. In such a case, the service should be implemented
as a
KUniqueApplication.

The value Multi for X-DCOP-ServiceType says that multiple
instances of the service can coexist, so every attempt to start the service
will create another process. As a last possibility the value None
can be used. In this case, a start of the service will not wait until it
is registered with DCOP.

X-KDE-StartupNotify should normally be set to false. Otherwise, when
the program is started, the task bar will show a startup notification, or, depending
on the user's settings, the cursor will be changed.

Note that the example of a DCOP call given here uses explit marshalling
of arguments. Often you will want to use a stub generated by dcopidl2cpp
instead, because it is much simpler and less error prone.

In the example given here, the service was started "by name", i.e. the
first argument to KApplication::startServiceByName() is the name is
appearing in the Name line of the desktop file. An alternative is to
use KApplication::startServiceByDesktopName(), which takes the file name
of its desktop file as argument, i.e. in this case "kio_uiserver.desktop".

All these calls take a list of URLs as a second argument, which is given
to the service on the command line. The third argument is a pointer to a
QString. If starting the service fails, this argument is set to a translated
error message.