RPC/IPC within Kde

Applications on a desktop usually need to communicate with eachother. Examples : ensure an application is launched only once, inform that a new icon is on the desktop, rebuild the mimetype database, open a link in browser, open mail reader, play that file with the mp3 player, script your applications, etc . IPC is what makes the desktop more coherent (by exchanging relevant information).

The requirements for a good IPC/RPC protocol are programming language independence because you don't know how an application is implemented evenghouth you want to communicate with it, and ease of use because this always a good thing (tm).

You can use Corba for that. Kde has done it in the past. But this is overkill. You get all the problems of Corba (resource consumption) just to get a few functions calls ! How do we do that without Corba ?

Kde RPC/IPC protocol : DCOP

Kde has developped DCOP, the Desktop COmmunication Protocol. DCOP is very lightweight and easy to use. It provides several advantages : All these keypoints makes DCOP the king RPC/IPC technology of kde. The examples will make clearer how easy and powerful it is.

Note that dcop doesn't depend upon Kde. So it can be used as a communication technlogy in any environment that wishes to use it.

DCOP in action

For our demonstration, we will use kwrite, the kde advanced editor and konqueror, the more-than-just-a-web-browser of kde.

Declaring a DCOP Interface

I said you needed two lines to declare a dcop interface. The first line is the word "K_DCOP", to put inside your class. The second is "k_dcop:" to put before your dcop slots (to use just like you use "public:").

In kde however, they prefer to declare the dcop interface separately, to make things cleaner. So almost all applications have a appnameIface.h file that only declares the dcop interface as pure virtual class. The main application class inherit from this interface class.

The following example shows the interface file of kwrite. There are many slots but I've hidden them with a [...] because they are not very intersting.
kwriteIface.h
#ifndef _KWRITE_IFACE_H_
#define _KWRITE_IFACE_H_
 
#include 
 
class KWriteIface : virtual public DCOPObject
{
   K_DCOP
 
k_dcop:

[...] 

   virtual int numLines()=0;
   virtual QString text()=0;
   virtual QString currentTextLine()=0;
   virtual QString textLine(int num)=0;
   virtual void insertText( const QString &txt, bool mark )=0; 
 
   virtual int currentLine()=0;
   virtual int currentColumn()=0;
 
   virtual bool loadFile(const QString &name, int flags = 0)=0;
   virtual bool writeFile(const QString &name)=0;

[...] 
};
#endif

Add the file kwriteIface.skel to your sources in the Makefile.am .
extract of Kwrite's Makfile.am
libkwritepart_la_SOURCES = highlight.cpp ktextprint.cpp  \
                   kwdoc.cpp kwview.cpp undohistory.cpp  \
                   kwdialog.cpp kwrite_factory.cpp \
                           kwbuffer.cpp kwtextline.cpp kvmallocator.cpp\
                           kwattribute.cpp kwriteIface.skel syntaxdocument.cpp

From that point, everything is automatic: dcopidl will generate from kwriteIface.h a xml file, kwriteIface.kidl which describes the dcop interface. dcipidl2cpp will generate the C++ code to handle the dcop calls for your application.

In your application, register your program when it is launched.
extract of kwview.cpp
  DCOPClient *client = kapp->dcopClient();
  if (!client->isRegistered())  // just in case we're embeeded
  {
    client->attach();
    client->registerAs("kwrite");
  }

Okay, that's it, kwrite is now able to receive dcop calls, as we will see.

Making a DCOP call with dcop

dcop is a shell tool to make dcop calls. Launch kwrite. We will play with dcop to insert a text line "this line was inserted with dcop". Here is how you an do it :

philippe@werewindle ~ $ dcop
kwin
kicker
kwrited
kded
kwrite-17199
konqueror-814
kcookiejar
klauncher
khotkeys
kmail
kxmlrpcd
kdesktop
klipper
ksmserver

philippe@werewindle ~ $ dcop kwrite
qt
KWriteIface

philippe@werewindle ~ $ dcop kwrite KWriteIface
QCStringList interfaces()
QCStringList functions()
void cursorLeft()
[...]
int numLines()
QString text()
QString currentTextLine()
QString textLine(int num)
QString currentWord()
QString word(int x,int y)
void insertText(QString txt,bool mark)
[...]

philippe@werewindle ~ $ dcop kwrite KWriteIface insertText "This text was inserted with a dcop call" false

philippe@werewindle ~ $

Look at kwrite. Ow, a line "This text was inserted with a dcop call" has appeared!

If you know the interface, you can make the last call directly. The first lines in this example were there just to show how to explore the dcop interfaces.

You realise what it means in terms of scriptability for kde. If you can do that with a simple shell tool, imagine what you can do with C++ programming !

Making a DCOP call with kdcop

kdcop is the same tool, but with a graphical frontend. The treeview makes it even easier to explore the dcop interfaces of your desktop.

Launch kwrite. Then launch kdcop, open kwrite-<pid>, open kwriteIface and double-click on the function void insertText(QString txt, bool mark). You should get something that looks like this screenshot :

Enter the text line "This text was inserted using kdcop!!!" and click "Ok". Look at kwrite. Ow, the line has appeared ! How magical !!!

Now, have fun. Launch all the kde program you usually use, and browse their interface with kdcop. Make calls and see what returns. Isn't that a cool !

C++ code to make a DCOP call

Let's imagine you are coding your kde application. You want to make a dcop call, for example to launch konqueror with a given url. How do you do that ?
Thank to dcopidl, this is trivial. Here is the c++ code that does it.
hop.cc
#include <dcopclient.h>
#include <kapp.h>
#include <KonquerorIface_stub.h>
 
int main(int argc, char ** argv)
{
    KApplication a(argc, argv, "hop" );
    kapp->dcopClient()->attach();

    // The stub class is a dcop implementation of KonquerorIface.
    KonquerorIface_stub konqy_dcop( "konqueror", "KonquerorIface");
 
    konqy_dcop.createNewWindow( QString("http://developer.kde.org") );
}
 
Makefile.am
bin_PROGRAMS = hop

INCLUDES= -I.. $(all_includes)

METASOURCES = AUTO
 
KonquerorIface_DIR =  $(srcdir)/..
 
hop_SOURCES = hop.cc KonquerorIface.stub
hop_LDFLAGS = $(KDE_RPATH) $(all_libraries)
hop_LDADD =  $(LIB_KFILE)

C code to make a DCOP call

If you are coding a C application, you can still easily make and receive dcop calls. Here how the same call looks like written in C:
#include "dcopc.h"
#include "marshal.h"
 
int main(int argc, char ** argv)
{
    DcopClient * client;
    dcop_data  * data;
    int success;
 
    gtk_init( &argc, &argv );
 
    client = dcop_client_new();
    dcop_client_attach(client);
 
    data = dcop_data_new();
    dcop_marshal_string16( data, "http://developer.kde.org" );
 
    success = dcop_client_send( client, "konqueror", "KonquerorIface",
                "createNewWindow(QString)", data );
}

Python code to make a DCOP call

But what if you program in python ?
Don't worry, just compile kdebindings/dcoppython and enjoy. It is even easier than in C++.
#!/usr/bin/env python
 
from dcop import *
 
app = DCOPApplication("kwrite")
app.KWriteIface.insertText("This text was inserted from a python shell!!!", 0)
 
app = DCOPApplication("konqueror")
app.KonquerorIface.createNewWindow("http://developer.kde.org")

Perl code to make a DCOP call

Perl programmer are not forgotten. Compile kdebindings/dcopperl and enjoy :
#!/usr/bin/perl
 
use DCOP;
 
my $client = new DCOP;
$client->attach();
 
# asynchronous
$client->send("konqueror", "KonquerorIface", "createNewWindow(QString)", "http://developer.kde.org" );
 
# synchronous
my $konqueror = $client->createObject("konqueror", "KonquerorIface");
$konqueror->createNewWindow("http://developer.kde.org");

Java code to make a DCOP call

But what if I program in java ? Well, there are bindings available, for dcop, kde and qt. .

Unfortunately I don't know java, so I can't provide tested code to launch konqueror or insert a line of text in kwrite. If someone can provide me with a working example like the aboves, I would be glad to include it in this page. :-)

sh script for XML-RPC DCOP call

But what if I program in language xyz that have no binding yet ? Well, in this case, you can try using the XML-RPC interface. Here is a code sample to make a dcop call with sh :

#!/bin/sh

port=`sed -e 's/,.*//' ~/.kxmlrpcd`
auth=`sed -e 's/.*,//' ~/.kxmlrpcd`


cat > cmd.xml <

  KWriteIface.insertText
    
      
        $auth
        This text was inserted using a XML-RPC deamon and a sh
script!!!
        false
      
  

EOF

length=`wc -c cmd.xml | sed -e 's/cmd.xml//;s/ //g'`

cat > head.xml <

Python code to make a DCOP call using xml-rpc

Of course, this is a lot easier if you have python and the xml-rpc library from pythonware.
Python 1.5.2 (#1, Sep 30 2000, 18:08:36)  [GCC 2.95.3 19991030 (prerelease)]
on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from xmlrpclib import *
>>> import os
>>>
>>> rc = open(os.environ['HOME'] + '/.kxmlrpcd', 'r')
>>> config = string.split(rc.read(), ',')
>>> port = config[0]
>>> auth = config[1]
>>>
>>> server = Server("http://localhost:" + port +"/kwrite")
>>> server.KWriteIface.insertText("This text was inserted using the xml-rpc dcop binding and python xml-rpc library !!!", 0);

Previous: Kde components

Next: Conclusions


Last modification : $Date: 2002/01/13 12:41:21 $ - $Author: philippe $