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 ?
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.
For our demonstration, we will use kwrite, the kde advanced editor and konqueror, the more-than-just-a-web-browser of kde.
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 |
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.
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 !
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 !
| 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) |
#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 );
}
|
#!/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")
|
#!/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");
|
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. :-)
#!/bin/sh port=`sed -e 's/,.*//' ~/.kxmlrpcd` auth=`sed -e 's/.*,//' ~/.kxmlrpcd` cat > cmd.xml < |
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 |