Translations of this tutorial

Building a Plugin Structure for a KDE application

Plugins give the capability of loading extra functionality into an
application at run time. Besides it being a cool thing to say that your
application has a plugin structure it also has certain real values:

It makes it possible to customize the application in ways the author
did not think of, or was not willing to build into the application.

It makes it possible to spread out responsibility and honor. Many
people work on Open Source programming due to the honor brings being
author of a piece of code. With plugins, a person can develop a
plugin, and claim it to be his baby.

It makes it possible to share code between unrelated applications, if
they just share a common plugin structure. An example would be image
viewers. Many different viewers exists, each with their unique
touch. Having a common plugin system, it might be possible to
develop plugins which would work in each of the viewers.

Finally, it makes it possible to make only part of an application
available if certain third party libraries or application is
not installed on a system.

This document will take you through all the steps required to build a plugin
structure for a KDE application. It might be worth reading even if you
are not writing a plugin structure for an application, but only wants to
write a plugin.

There exist two kinds of plugins:

The first kind of plugins implements an interface specified by the
host application.[1]
This is similar to implementing a pure virtual
class, with the exception that the class is in a dynamic loadable
library. This kind of plugins are often referred to as loadable
modules.
Examples of loadable modules are image format loaders,
syntax highlighters, and import/export of data to different
formats.
The advantages of these plugins is that third parties can
add new formats to the application. E.g. an editor can highlight
code written in say the VDM-SL language, even though the author of
the editor had no clue such a language existed.

The second kind of plugins brings new functionality and actions for
menu bars and tool bars.
Examples of this kind of plugin include generating HTML from an
image manager (which previously had no HTML export features), adding
spell checking to an editor, and adding web page validation to a web
browser.
The advantage of this kind of plugins is that extra functionality
can be added by third parties with the implications described in the
section above.

The main focus of this document is on the second kind, but whenever
applicable comments will be added regarding difference between the two kinds.

The demo application

Along with this document is an example application with a plugin
structure. The application can be downloaded here, and throughout the document,
I will refer to this application as a running example.

The files are separated in to three directories:

application - this directory
contains the application which loads a plugin.

Implement the plugins using the interface to communication with the application.

The order things are described in this document is slightly different from
the order above, simply to make it more comprehensive.

We will start by looking at how all of the above is accomplished from a C++
point of view, and then we will look at what kind of support
files is needed. I.e. Makefile.am's and desktop files.

Base class for plugins

Below the hood of KDE, the following is roughly going on when a plugin is
loaded: The application finds the dynamic library which is the plugin,
the library is dlopen'd, and a special C function is searched for, and when
found executed. The C function must return something that is valuable to
the program loading the plugin. In KDE it must return a pointer to a base
class defined by the calling function.

In other words, the result of loading the plugin is a pointer to a class,
which the loading application knows about. Therefore our very first task
when developing a plugin structure is to create a virtual base class for
the plugins to implement.

In our sample application, this class is interfaces/plugindemo/plugin.h.
The class is, as all the files in the template subdirectory, in the
namespace PluginDemo. This is
not a requirement but merely to avoid name clashes, and make it more
obvious where things comes from. Likewise the file is in the subdirectory
plugindemo, which means that including the file is done by an
include statement like #include <plugindemo/plugin>, this is
also done only to avoid name clashes.

The important thing to notice in this class is that it inherits
KXMLGUIClient. This has the consequence that the host application
may merge its tools bar and menu bar actions with those from the
plugin. We will discuss how that is done later.

Two other methods are defined in this class, namely
editorInterface(), and selectionInterface(). For the
plugin to be useful at all, it must be able to access the
internals of the applications it is plugged into. These two methods return
an interface for this. The interface is implemented, as we will later see, by the host application. For now just
accept that the plugins may access the host application though the two
classes which can be found in interfaces/plugindemo/editorinterface.h
and interfaces/plugindemo/selectioninterface.h.

Had this been a loadable module then you would have
seen these differences:

We would not have inherited from KXMLGUIClient.

One or more functions for doing what the plugin has to do would be
added to the interface. E.g. a decode() function for an
image format decoder.

We might not need the interface methods (e.g editorInterface() ),
as the plugin specific functions (e.g. the decode() function) are
initiated by the host application, and it might bring all relevant
information to the plugin as arguments to the action functions.

The first thing to notice is that the class inherits from
PluginDemo::Plugin, this is a requirement for it to be a plugin at
all. From a C++ point of view this class is just a normal C++ class, which
of course can implement slots, have instance variables, connect to signals
etc. The only tricky thing about this class is how it is constructed. It is
namely constructed by the host application dynamically loading the library
defining it, and by a set of indirections, creating an instance of it.

To understand how all this works, please look at the implementation (plugindemo-capitalize/capitalizeplugin.cpp).
The crucial point is to be found at the top of the file, where you will see these lines of code:

This reveals that the macro expands to a definition of a C function[2], and that function returns an
instance of the class defined by the second argument to the macro.

The second argument is itself a bit of magic, it says:

KGenericFactory<CapitalizePlugin>( "plugindemo_capitalize" )

KGenericFactory is a template, which takes another class for template
instantiation. The KGenericFactory has a method needed for all this to fit
together, namely a create() method. The create method will when called from
the KDE subsystem, create an instance of the class that was given to it during
template instantiation.

If all this is black magic to you, or it seems overly complex, then don't
bother, its not that important for developing plugins, but was merely included
here for those wanting to know all the gory details. All you need to
remember are these steps:

Insert the K_EXPORT_COMPONENT_FACTORY in the .cpp file defining
the plugin class.

As the first argument to the macro, you must specify the library file in
which this class resides (without the lib prefix, and
without the .la extension.)

As the second argument, you must inside the angle brackets specify the
name of the class you define in the plugin.

Finally, inside the braces you must specify a unique string, which
will be given to creation of the KInstance
object. KInstance is responsible for specifying where
translation files, and configuration files are found among other things.

Merging GUI with the host application

What they basically do are to load the user interface of the plugin,
and merge it with the host application. That means that the plugin may add
items to the menu bar and tool bars of the host application.

The name specified as an argument to the setXMLFile() method is a
resource file, which must be installed by the Makefile.am file for the
plugin. The details about Makefile.am will be explained later. The content of this file is described in this howto.

The resource
file specifies one action to be put into the Edit menu bar
item. This actions must be set up in the constructor. For that an instance
of KApplication is being created.

Communicating with the host application

The plugin communication with the host application happens through the
interfaces available from the super class (interfaces/plugindemo/plugin.h). An
example of this can be seen in the slotCapitalize() method, which is
invoked when the user selects our action in the menu bar. In this method, we
ask the selection interface for the text of the selection, capitalize the
words in that text, and set back the text.

Loading plugins

Its now time to look at the host application which can be found in application/mainwindow.cpp, and
reveal how easy it is to actually load the plugins.

First thing to notice about the host application is that its main window
inherits KMainWindow. This is needed for the merging of menu bar
and tool bars.

Loading the plugins are done in the function loadPlugins(). The
first line of that function is:

What this basically does is to ask the KDE system to return a list of
plugins which matches the type PluginDemo/Plugin. Each plugin must
come with a desktop file which specify the plugins type. We will later discuss the desktop file in details.

Next we iterate through the list of plugins, and load each of them in turn.
This is done with this code:

All the hardcore stuff regarding dlopening the file and calling the init
function is taken care of by the
createInstanceFromService funciton.

Many things can happen when loading a plugin, for one thing it might not
even be a plugin for our application, the user might have mistyped
something in the desktop file etc. Therefore it is very important to check
that we actually got a pointer back from the
createInstanceFromService() method. We should of course notify the
user if we did not manage to load a plugin, but for simplicity we skip that
part here.

You might notice that the second parameter to the
createInstanceFromService() method is _pluginInterface,
this is the parent pointer, but in this framework it needs to be special in
ways described in the next section.

Once the plugin has been loaded, it is time to add it to the KXML factory, so
that the GUI of the plugin is merged into the host application. This is
done by the method call:

guiFactory()->addClient(plugin);

Communication between the plugins and the host applications.

Until now we have accepted that communication between the plugin and the host application has
been through the interface methods which is returned by the Plugin
class from interfaces/plugindemo/plugin.h.

These functions do, however, need to get their information out of the host
application one way or the other, which is what we will discuss now.

Looking at the implementation of the selectionInterface() function will show us
how this happens:

The plugin simply asks its parent for the child inheriting the class
selectionInterface. In other words the parent need to set up an
instance of a class which inherits SelectionInterface.
How the actual setup is done, can be seen from the application/mainwindow.cpp
file:

In the above we create the _pluginInterface object, and give it
a pointer to the class MySelectionInterface. The
_pluginInterface pointer is later given as a parent when
constructing the plugins (as we saw above):

In the above code you might have noticed that no interface was implemented
for the EditorInterface class. This is to illustrate how one kind
of host application might implement only some of the interfaces. Imagine
that the plugins was shared among a number of applications. Not all
application may be able to offer the full interface. Plugins requiring a
given non-implemented interface will of course not work when that interface
is not implemented. Thus, when a plugin needs access to a certain interface,
it should first check if the interface is available, and if not disable itself.

At this point, I'm pretty sure you ask yourself, why on earth was the
communication between plugins and host application implemented that way?
To be honest with you, this was indeed not my initial idea either. My
initial suggestion was something along the line of a virtual
PluginInterface class, which the plugins were given a pointer to upon creation,
and which the base application had to implement.

This was indeed much clearer, and a much better design object oriented
wise, but it had a fatal drawback, namely it made it impossible to add new
interfaces in a binary compatible way. Thus binary compatibility was broken
when a new interface was added, and all plugins had to be recompiled.

With the current solution, a new interface means a new non-virtual
method needs to be added to the
plugin.h file. An example would be a paragraph interface, which
would result in a method returning a pointer to such a
ParapgrahInterface class. Adding a non-virtual method to a class
can be done without breaking binary compatibility.

Auxiliary files needed to make everything work.

Now you know how all the pieces of code fits together to develop a plugin
structure for your application. That's not enough, however, the KDE
frame work must be told that the code you compile as a plugin actually is a
plugin, and you need to tell KDE about your new plugin type, which was used
by KTrader above. This is what this section
will talk about.

The interface directory

The Makefile.am in interfaces/plugindemo
needs a single addition compared to a normal Makefile.am for a library,
namely the following line:

kde_servicetypes_DATA = plugindemoplugin.desktop

What this line says is that the file plugindemoplugin.desktop must
be installed on the system as a service type.

Remember, when we loaded the plugins, we did not specify any directory for
KDE to search for plugins, nor did we specify any files to look in. All we
specified was that we wanted to load a plugin of the service type
PluginDemo/Plugin.
The file interfaces/plugindemo/plugindemoplugin.desktop
describes the service type:

Plugins are different from libraries in the sense that they do not contain
version information, just like you don't specify that you want to load
konqueror version x.y, but only that you want to load konqueror. Likewise
you do not specify that you want to load a specific version of a
plugin[3]. For that reason
you need to add the -module parameter to the LDFLAGS.

As we saw above, the plugin has a resource file for the
GUI that comes with the plugin, that resource file do of course need to be
installed on the system. which is what the above lines ensures.

Note, the file name you specify for the argument to setXMLFile()
must match the filename in the plugins_DATA line, and the name you
specify as an argument to KGenericFactory as part of the
K_EXPORT_COMPONENT_FACTORY macro, must match the name in the
plugins_DATA section.

kde_services_DATA = plugindemo_capitalize.desktop

Finally, the plugin needs a desktop file, which we specify in the above
line. The desktop
file, looks like this:

There are three important things to note about this file. First the
ServiceTypes line must match the service type specified in the
desktop file in the interfaces directory. Second, the
X-KDE-Library must specify the name of the library that the plugin
is compiled into, without the postfix .la. Finally, it's no
coincidence that the filename has plugindemo_ as prefix. Bad things
are bound to happen if two applications installs a desktop file with the
same name, therefore you should always namespace them by prefixing
them as done above.

Conclusion

You have now seen an example of how to build a plugin system for your KDE
application, We have discussed the advantages of plugins, but we haven't
discussed how to design your interface classes. This is
pretty similar to designing the interface of a library. You should be
careful what you make available to the plugin. Making too little available
to the plugin will render the plugin interface useless. Making too much
available to the plugins will make it impossible to change anything in the
host application without breaking plugins.

Notes

Note 1: The application which
loads the plugin will in this document be named the host application.

Note 2: It is a C function simply so
that no C++ name mangling takes place, and it therefore is easier to
dlopen.

Note 3: If you need to specify a
version number for a plugin for one reason or another, then do it with an
attribute in the plugin file.

Credits

A large thank you goes to David Faure and Simon Hausmann who both have answered a lot of
questions plus helped me debug code, to make this document possible.