diff options
author | Havoc Pennington <hp@redhat.com> | 2002-11-21 16:41:33 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2002-11-21 16:41:33 +0000 |
commit | 93cff3d69fb705806d2af4fd6f29c497ea3192e0 (patch) | |
tree | 628cfe4b38bcd3c2fba071e9dd528459b75571e4 | |
parent | 47229f733179ed2f9f5e8baffe855f09530ff94f (diff) |
initial import of "dbus" skeleton
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 121 | ||||
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | INSTALL | 0 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | acconfig.h | 10 | ||||
-rwxr-xr-x | autogen.sh | 70 | ||||
-rw-r--r-- | configure.in | 64 | ||||
-rw-r--r-- | dbus-1.0.pc.in | 11 | ||||
-rw-r--r-- | dbus/Makefile.am | 22 | ||||
-rw-r--r-- | dbus/dbus-hash.c | 0 | ||||
-rw-r--r-- | dbus/dbus-message.c | 0 | ||||
-rw-r--r-- | dbus/dbus.h | 0 | ||||
-rw-r--r-- | doc/Makefile.am | 7 | ||||
-rw-r--r-- | doc/dcop-howto.txt | 559 | ||||
-rw-r--r-- | server/Makefile.am | 15 | ||||
-rw-r--r-- | server/main.c | 8 | ||||
-rw-r--r-- | test/Makefile.am | 0 |
20 files changed, 897 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..3bc119cb --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Havoc Pennington <hp@redhat.com>
\ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..79037fa6 --- /dev/null +++ b/COPYING @@ -0,0 +1,121 @@ + Academic Free License + Version 1.2 + +This Academic Free License applies to any original work of authorship +(the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the +Original Work: + +Licensed under the Academic Free License version 1.2 + +Grant of License. Licensor hereby grants to any person obtaining a +copy of the Original Work ("You") a world-wide, royalty-free, +non-exclusive, perpetual, non-sublicenseable license (1) to use, copy, +modify, merge, publish, perform, distribute and/or sell copies of the +Original Work and derivative works thereof, and (2) under patent claims +owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for +sale the Original Work and derivative works thereof, subject to the +following conditions. + +Attribution Rights. You must retain, in the Source Code of any +Derivative Works that You create, all copyright, patent or trademark +notices from the Source Code of the Original Work, as well as any +notices of licensing and any descriptive text identified therein as an +"Attribution Notice." You must cause the Source Code for any Derivative +Works that You create to carry a prominent Attribution Notice reasonably +calculated to inform recipients that You have modified the Original Work. + +Exclusions from License Grant. Neither the names of Licensor, nor the +names of any contributors to the Original Work, nor any of their +trademarks or service marks, may be used to endorse or promote products +derived from this Original Work without express prior written permission +of the Licensor. + +Warranty and Disclaimer of Warranty. Licensor warrants that the copyright +in and to the Original Work is owned by the Licensor or that the Original +Work is distributed by Licensor under a valid current license from the +copyright owner. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" +BASIS and WITHOUT WARRANTY, either express or implied, including, without +limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL +WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part +of this License. No license to Original Work is granted hereunder except +under this disclaimer. + +Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, +incidental, or consequential damages of any character arising as a result +of this License or the use of the Original Work including, without +limitation, damages for loss of goodwill, work stoppage, computer failure +or malfunction, or any and all other commercial damages or losses. This +limitation of liability shall not apply to liability for death or personal +injury resulting from Licensor's negligence to the extent applicable law +prohibits such limitation. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so this exclusion and +limitation may not apply to You. + +License to Source Code. The term "Source Code" means the preferred form of +the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for +as long as Licensor continues to distribute the Original Work, and by +publishing the address of that information repository in a notice immediately +following the copyright notice that applies to the Original Work. + +Mutual Termination for Patent Action. This License shall terminate +automatically and You may no longer exercise any of the rights granted to You +by this License if You file a lawsuit in any court alleging that any OSI +Certified open source software that is licensed under any license containing +this "Mutual Termination for Patent Action" clause infringes any patent +claims that are essential to use that software. + +Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises +not to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. + +-- +END OF LICENSE. The following is intended to describe the essential +differences between the Academic Free License (AFL) version 1.0 and other +open source licenses: + +The Academic Free License is similar to the BSD, MIT, UoI/NCSA and Apache +licenses in many respects but it is intended to solve a few problems with +those licenses. + +* The AFL is written so as to make it clear what software is being +licensed (by the inclusion of a statement following the copyright notice +in the software). This way, the license functions better than a template +license. The BSD, MIT and UoI/NCSA licenses apply to unidentified software. + +* The AFL contains a complete copyright grant to the software. The BSD +and Apache licenses are vague and incomplete in that respect. + +* The AFL contains a complete patent grant to the software. The BSD, MIT, +UoI/NCSA and Apache licenses rely on an implied patent license and contain +no explicit patent grant. + +* The AFL makes it clear that no trademark rights are granted to the +licensor's trademarks. The Apache license contains such a provision, but the +BSD, MIT and UoI/NCSA licenses do not. + +* The AFL includes the warranty by the licensor that it either owns the +copyright or that it is distributing the software under a license. None of +the other licenses contain that warranty. All other warranties are disclaimed, +as is the case for the other licenses. + +* The AFL is itself copyrighted (with the right granted to copy and distribute +without modification). This ensures that the owner of the copyright to the +license will control changes. The Apache license contains a copyright notice, +but the BSD, MIT and UoI/NCSA licenses do not. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..aa6579d3 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +2002-11-21 Havoc Pennington <hp@redhat.com> + + * Initial module creation + diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/INSTALL diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..e35a6de5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS=dbus server test doc + @@ -0,0 +1 @@ +No news.
\ No newline at end of file @@ -0,0 +1 @@ +D-BUS is a simple IPC library based on messages. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 00000000..e100ec29 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,10 @@ +#undef PACKAGE +#undef VERSION +#undef HAVE_CATGETS +#undef HAVE_GETTEXT +#undef HAVE_LC_MESSAGES +#undef HAVE_STPCPY +#undef ENABLE_NLS +#undef HAVE_PTHREAD_H +#undef GETTEXT_PACKAGE +#undef SANE_MALLOC_PROTOS diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..3b74d828 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +PROJECT=dbus +TEST_TYPE=-f +FILE=dbus-1.0.pc.in + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +AUTOMAKE=automake-1.6 +ACLOCAL=aclocal-1.6 + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + AUTOMAKE=automake + ACLOCAL=aclocal +} + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROJECT." + echo "Get ftp://ftp.cygnus.com/pub/home/tromey/automake-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +test $TEST_TYPE $FILE || { + echo "You must run this script in the top-level $PROJECT directory" + exit 1 +} + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +libtoolize --copy --force + +echo $ACLOCAL $ACLOCAL_FLAGS +$ACLOCAL $ACLOCAL_FLAGS + +# optionally feature autoheader +(autoheader --version) < /dev/null > /dev/null 2>&1 && autoheader + +$AUTOMAKE -a $am_opt +autoconf || echo "autoconf failed - version 2.5x is probably required" + +cd $ORIGDIR + +$srcdir/configure --enable-maintainer-mode "$@" + +echo +echo "Now type 'make' to compile $PROJECT." diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..18f8f967 --- /dev/null +++ b/configure.in @@ -0,0 +1,64 @@ +AC_INIT(dbus/dbus.h) + +AM_CONFIG_HEADER(config.h) + +AM_INIT_AUTOMAKE(dbus, 0.1) + +# Honor aclocal flags +ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS" + +GETTEXT_PACKAGE=dbus-1 +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE") + +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_ISC_POSIX +AC_HEADER_STDC +AC_ARG_PROGRAM +AM_PROG_LIBTOOL + +AC_ARG_ENABLE(qt, [ --disable-qt disable Qt-friendly client library],enable_qt=no,enable_qt=yes) +AC_ARG_ENABLE(glib, [ --disable-glib disable GLib-friendly client library],enable_glib=no,enable_glib=yes) + +changequote(,)dnl +if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac +fi +changequote([,])dnl + +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(void *) +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(__int64) + +## byte order +AC_C_BIGENDIAN + +AC_CHECK_FUNCS(vsnprintf vasprintf) + +DBUS_CLIENT_CFLAGS= +DBUS_CLIENT_LIBS= +AC_SUBST(DBUS_CLIENT_CFLAGS) +AC_SUBST(DBUS_CLIENT_LIBS) + +DBUS_SERVER_CFLAGS= +DBUS_SERVER_LIBS= +AC_SUBST(DBUS_SERVER_CFLAGS) +AC_SUBST(DBUS_SERVER_LIBS) + +AC_OUTPUT([ +Makefile +dbus/Makefile +server/Makefile +test/Makefile +doc/Makefile +dbus-1.0.pc +]) diff --git a/dbus-1.0.pc.in b/dbus-1.0.pc.in new file mode 100644 index 00000000..2df0a7cf --- /dev/null +++ b/dbus-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: dbus +Description: Free desktop message bus +Version: @VERSION@ +Libs: -L${libdir} -ldbus-1 +Cflags: -I${includedir}/dbus-1.0 + diff --git a/dbus/Makefile.am b/dbus/Makefile.am new file mode 100644 index 00000000..09dd84f8 --- /dev/null +++ b/dbus/Makefile.am @@ -0,0 +1,22 @@ + +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) + +dbusincludedir=$(includedir)/dbus-1.0/dbus + +lib_LTLIBRARIES=libdbus-1.la + +dbusinclude_HEADERS= \ + dbus.h + +libdbus_1_la_SOURCES= \ + dbus-message.c + +noinst_LTLIBRARIES=libdbus-convenience.la + +libdbus_convenience_la_SOURCES= \ + dbus-hash.c + +libdbus_1_la_LIBADD= $(DBUS_CLIENT_LIBS) libdbus-convenience.la +## don't export symbols that start with "_" (we use this +## convention for internal symbols) +libdbus_1_la_LDFLAGS= -export-symbols-regex "^[[^_]].*" diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dbus/dbus-hash.c diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dbus/dbus-message.c diff --git a/dbus/dbus.h b/dbus/dbus.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dbus/dbus.h diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..d84c70be --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST= \ + dcop-howto.txt \ + fdmb-design.txt \ + fdmb-messages.txt \ + fdmb-properties.txt \ + message-delivery.txt \ + message-matching.txt diff --git a/doc/dcop-howto.txt b/doc/dcop-howto.txt new file mode 100644 index 00000000..dfd3bcf8 --- /dev/null +++ b/doc/dcop-howto.txt @@ -0,0 +1,559 @@ + DCOP: Desktop COmmunications Protocol + + Preston Brown <pbrown@kde.org> + October 14, 1999 + + Revised and extended by Matthias Ettrich <ettrich@kde.org> + Mar 29, 2000 + + Extended with DCOP Signals by Waldo Bastian <bastian@kde.org> + Feb 19, 2001 + + +Motivation and Background: +-------------------------- + +The motivation behind building a protocol like DCOP is simple. For +the past year, we have been attempting to enable interprocess +communication between KDE applications. KDE already has an extremely +simple IPC mechanism called KWMcom, which is (was!) used for communicating +between the panel and the window manager for instance. It is about as +simple as it gets, passing messages via X Atoms. For this reason it +is limited in the size and complexity of the data that can be passed +(X atoms must be small to remain efficient) and it also makes it so +that X is required. CORBA was thought to be a more effective IPC/RPC +solution. However, after a year of attempting to make heavy use of +CORBA in KDE, we have realized that it is a bit slow and memory +intensive for simple use. It also has no authentication available. + +What we really needed was an extremely simple protocol with basic +authorization, along the lines of MIT-MAGIC-COOKIE, as used by X. It +would not be able to do NEARLY what CORBA was able to do, but for the +simple tasks required it would be sufficient. Some examples of such +tasks might be an application sending a message to the panel saying, +"I have started, stop displaying the 'application starting' wait +state," or having a new application that starts query to see if any +other applications of the same name are running. If they are, simply +call a function on the remote application to create a new window, +rather than starting a new process. + +Implementation: +--------------- + +DCOP is a simple IPC/RPC mechanism built to operate over sockets. +Either unix domain sockets or tcp/ip sockets are supported. DCOP is +built on top of the Inter Client Exchange (ICE) protocol, which comes +standard as a part of X11R6 and later. It also depends on Qt, but +beyond that it does not require any other libraries. Because of this, +it is extremely lightweight, enabling it to be linked into all KDE +applications with low overhead. + +Model: +------ + +The model is simple. Each application using DCOP is a client. They +communicate to each other through a DCOP server, which functions like +a traffic director, dispatching messages/calls to the proper +destinations. All clients are peers of each other. + +Two types of actions are possible with DCOP: "send and forget" +messages, which do not block, and "calls," which block waiting for +some data to be returned. + +Any data that will be sent is serialized (marshalled, for you CORBA +types) using the built-in QDataStream operators available in all of +the Qt classes. This is fast and easy. In fact it's so little work +that you can easily write the marshalling code by hand. In addition, +there's a simple IDL-like compiler available (dcopidl and dcopidl2cpp) +that generates stubs and skeletons for you. Using the dcopidl compiler +has the additional benefit of type safety. + +This HOWTO describes the manual method first and covers the dcopidl +compiler later. + +Establishing the Connection: +---------------------------- + +KApplication has gained a method called "KApplication::dcopClient()" +which returns a pointer to a DCOPClient instance. The first time this +method is called, the client class will be created. DCOPClients have +unique identifiers attached to them which are based on what +KApplication::name() returns. In fact, if there is only a single +instance of the program running, the appId will be equal to +KApplication::name(). + +To actually enable DCOP communication to begin, you must use +DCOPClient::attach(). This will attempt to attach to the DCOP server. +If no server is found or there is any other type of error, attach() +will return false. KApplication will catch a dcop signal and display an +appropriate error message box in that case. + +After connecting with the server via DCOPClient::attach(), you need to +register this appId with the server so it knows about you. Otherwise, +you are communicating anonymously. Use the +DCOPClient::registerAs(const QCString &name) to do so. In the simple +case: + +/* + * returns the appId that is actually registered, which _may_ be + * different from what you passed + */ +appId = client->registerAs(kApp->name()); + +If you never retrieve the DCOPClient pointer from KApplication, the +object will not be created and thus there will be no memory overhead. + +You may also detach from the server by calling DCOPClient::detach(). +If you wish to attach again you will need to re-register as well. If +you only wish to change the ID under which you are registered, simply +call DCOPClient::registerAs() with the new name. + +KUniqueApplication automatically registers itself to DCOP. If you +are using KUniqueApplication you should not attach or register +yourself, this is already done. The appId is by definition +equal to kapp->name(). You can retrieve the registered DCOP client +by calling kapp->dcopClient(). + +Sending Data to a Remote Application: +------------------------------------- + +To actually communicate, you have one of two choices. You may either +call the "send" or the "call" method. Both methods require three +identification parameters: an application identifier, a remote object, +a remote function. Sending is asynchronous (i.e. it returns immediately) +and may or may not result in your own application being sent a message at +some point in the future. Then "send" requires one and "call" requires +two data parameters. + +The remote object must be specified as an object hierarchy. That is, +if the toplevel object is called "fooObject" and has the child +"barObject", you would reference this object as "fooObject/barObject". +Functions must be described by a full function signature. If the +remote function is called "doIt", and it takes an int, it would be +described as "doIt(int)". Please note that the return type is not +specified here, as it is not part of the function signature (or at +least the C++ understanding of a function signature). You will get +the return type of a function back as an extra parameter to +DCOPClient::call(). See the section on call() for more details. + +In order to actually get the data to the remote client, it must be +"serialized" via a QDataStream operating on a QByteArray. This is how +the data parameter is "built". A few examples will make clear how this +works. + +Say you want to call "doIt" as described above, and not block (or wait +for a response). You will not receive the return value of the remotely +called function, but you will not hang while the RPC is processed either. +The return value of send() indicates whether DCOP communication succeeded +or not. + +QByteArray data; +QDataStream arg(data, IO_WriteOnly); +arg << 5; +if (!client->send("someAppId", "fooObject/barObject", "doIt(int)", + data)) + qDebug("there was some error using DCOP."); + +OK, now let's say we wanted to get the data back from the remotely +called function. You have to execute a call() instead of a send(). +The returned value will then be available in the data parameter "reply". +The actual return value of call() is still whether or not DCOP +communication was successful. + +QByteArray data, replyData; +QCString replyType; +QDataStream arg(data, IO_WriteOnly); +arg << 5; +if (!client->call("someAppId", "fooObject/barObject", "doIt(int)", + data, replyType, replyData)) + qDebug("there was some error using DCOP."); +else { + QDataStream reply(replyData, IO_ReadOnly); + if (replyType == "QString") { + QString result; + reply >> result; + print("the result is: %s",result.latin1()); + } else + qDebug("doIt returned an unexpected type of reply!"); +} + +N.B.: You cannot call() a method belonging to an application which has +registered with an unique numeric id appended to its textual name (see +dcopclient.h for more info). In this case, DCOP would not know which +application it should connect with to call the method. This is not an issue +with send(), as you can broadcast to all applications that have registered +with appname-<numeric_id> by using a wildcard (e.g. 'konsole-*'), which +will send your signal to all applications called 'konsole'. + +Receiving Data via DCOP: +------------------------ + +Currently the only real way to receive data from DCOP is to multiply +inherit from the normal class that you are inheriting (usually some +sort of QWidget subclass or QObject) as well as the DCOPObject class. +DCOPObject provides one very important method: DCOPObject::process(). +This is a pure virtual method that you must implement in order to +process DCOP messages that you receive. It takes a function +signature, QByteArray of parameters, and a reference to a QByteArray +for the reply data that you must fill in. + +Think of DCOPObject::process() as a sort of dispatch agent. In the +future, there will probably be a precompiler for your sources to write +this method for you. However, until that point you need to examine +the incoming function signature and take action accordingly. Here is +an example implementation. + +bool BarObject::process(const QCString &fun, const QByteArray &data, + QCString &replyType, QByteArray &replyData) +{ + if (fun == "doIt(int)") { + QDataStream arg(data, IO_ReadOnly); + int i; // parameter + arg >> i; + QString result = self->doIt (i); + QDataStream reply(replyData, IO_WriteOnly); + reply << result; + replyType = "QString"; + return true; + } else { + qDebug("unknown function call to BarObject::process()"); + return false; + } +} + +Receiving Calls and processing them: +------------------------------------ + +If your applications is able to process incoming function calls +right away the above code is all you need. When your application +needs to do more complex tasks you might want to do the processing +out of 'process' function call and send the result back later when +it becomes available. + +For this you can ask your DCOPClient for a transactionId. You can +then return from the 'process' function and when the result is +available finish the transaction. In the mean time your application +can receive incoming DCOP function calls from other clients. + +Such code could like this: + +bool BarObject::process(const QCString &fun, const QByteArray &data, + QCString &, QByteArray &) +{ + if (fun == "doIt(int)") { + QDataStream arg(data, IO_ReadOnly); + int i; // parameter + arg >> i; + QString result = self->doIt(i); + + DCOPClientTransaction *myTransaction; + myTransaction = kapp->dcopClient()->beginTransaction(); + + // start processing... + // Calls slotProcessingDone when finished. + startProcessing( myTransaction, i); + + return true; + } else { + qDebug("unknown function call to BarObject::process()"); + return false; + } +} + +slotProcessingDone(DCOPClientTransaction *myTransaction, const QString &result) +{ + QCString replyType = "QString"; + QByteArray replyData; + QDataStream reply(replyData, IO_WriteOnly); + reply << result; + kapp->dcopClient()->endTransaction( myTransaction, replyType, replyData ); +} + +DCOP Signals +------------ + +Sometimes a component wants to send notifications via DCOP to other +components but does not know which components will be interested in these +notifications. One could use a broadcast in such a case but this is a very +crude method. For a more sophisticated method DCOP signals have been invented. + +DCOP signals are very similair to Qt signals, there are some differences +though. A DCOP signal can be connected to a DCOP function. Whenever the DCOP +signal gets emitted, the DCOP functions to which the signal is connected are +being called. DCOP signals are, just like Qt signals, one way. They do not +provide a return value. + +A DCOP signal originates from a DCOP Object/DCOP Client combination (sender). +It can be connected to a function of another DCOP Object/DCOP Client +combination (receiver). + +There are two major differences between connections of Qt signals and +connections of DCOP signals. In DCOP, unlike Qt, a signal connections can +have an anonymous sender and, unlike Qt, a DCOP signal connection can be +non-volatile. + +With DCOP one can connect a signal without specifying the sending DCOP Object +or DCOP Client. In that case signals from any DCOP Object and/or DCOP Client +will be delivered. This allows the specification of certain events without +tying oneself to a certain object that implementes the events. + +Another DCOP feature are so called non-volatile connections. With Qt signal +connections, the connection gets deleted when either sender or receiver of +the signal gets deleted. A volatile DCOP signal connection will behave the +same. However, a non-volatile DCOP signal connection will not get deleted +when the sending object gets deleted. Once a new object gets created with +the same name as the original sending object, the connection will be restored. +There is no difference between the two when the receiving object gets deleted, +in that case the signal connection will always be deleted. + +A receiver can create a non-volatile connection while the sender doesn't (yet) +exist. An anonymous DCOP connection should always be non-volatile. + +The following example shows how KLauncher emits a signal whenever it notices +that an application that was started via KLauncher terminates. + + QByteArray params; + QDataStream stream(params, IO_WriteOnly); + stream << pid; + kapp->dcopClient()->emitDCOPSignal("clientDied(pid_t)", params); + +The task manager of the KDE panel connects to this signal. It uses an +anonymous connection (it doesn't require that the signal is being emitted +by KLauncher) that is non-volatile: + + connectDCOPSignal(0, 0, "clientDied(pid_t)", "clientDied(pid_t)", false); + +It connects the clientDied(pid_t) signal to its own clientDied(pid_t) DCOP +function. In this case the signal and the function to call have the same name. +This isn't needed as long as the arguments of both signal and receiving function +match. The receiving function may ignore one or more of the trailing arguments +of the signal. E.g. it is allowed to connect the clientDied(pid_t) signal to +a clientDied(void) DCOP function. + +Using the dcopidl compiler +--------------------- + +dcopidl makes setting up a DCOP server easy. Instead of having to implement +the process() method and unmarshalling (retrieving from QByteArray) parameters +manually, you can let dcopidl create the necessary code on your behalf. + +This also allows you to describe the interface for your class in a +single, separate header file. + +Writing an IDL file is very similar to writing a normal C++ header. An +exception is the keyword 'ASYNC'. It indicates that a call to this +function shall be processed asynchronously. For the C++ compiler, it +expands to 'void'. + +Example: + +#ifndef MY_INTERFACE_H +#define MY_INTERFACE_H + +#include <dcopobject.h> + +class MyInterface : virtual public DCOPObject +{ + K_DCOP + + k_dcop: + + virtual ASYNC myAsynchronousMethod(QString someParameter) = 0; + virtual QRect mySynchronousMethod() = 0; +}; + +#endif + +As you can see, you're essentially declaring an abstract base class, which +virtually inherits from DCOPObject. + +If you're using the standard KDE build scripts, then you can simply +add this file (which you would call MyInterface.h) to your sources +directory. Then you edit your Makefile.am, adding 'MyInterface.skel' +to your SOURCES list and MyInterface.h to include_HEADERS. + +The build scripts will use dcopidl to parse MyInterface.h, converting +it to an XML description in MyInterface.kidl. Next, a file called +MyInterface_skel.cpp will automatically be created, compiled and +linked with your binary. + +The next thing you have to do is to choose which of your classes will +implement the interface described in MyInterface.h. Alter the inheritance +of this class such that it virtually inherits from MyInterface. Then +add declarations to your class interface similar to those on MyInterface.h, +but virtual, not pure virtual. + +Example: + +class MyClass: public QObject, virtual public MyInterface +{ + Q_OBJECT + + public: + MyClass(); + ~MyClass(); + + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; + +Note: (Qt issue) Remember that if you are inheriting from QObject, you must +place it first in the list of inherited classes. + +In the implementation of your class' ctor, you must explicitly initialize +those classes from which you are inheriting from. This is, of course, good +practise, but it is essential here as you need to tell DCOPObject the name of +the interface which your are implementing. + +Example: + +MyClass::MyClass() + : QObject(), + DCOPObject("MyInterface") +{ + // whatever... +} + +Now you can simply implement the methods you have declared in your interface, +exactly the same as you would normally. + +Example: + +void MyClass::myAsynchronousMethod(QString someParameter) +{ + qDebug("myAsyncMethod called with param `" + someParameter + "'"); +} + + +It is not necessary (though very clean) to define an interface as an +abstract class of its own, like we did in the example above. We could +just as well have defined a k_dcop section directly within MyClass: + +class MyClass: public QObject, virtual public DCOPObject +{ + Q_OBJECT + K_DCOP + + public: + MyClass(); + ~MyClass(); + + k_dcop: + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; + +In addition to skeletons, dcopidl2cpp also generate stubs. Those make +it easy to call a DCOP interface without doing the marshalling +manually. To use a stub, add MyInterface.stub to the SOURCES list of +your Makefile.am. The stub class will then be called MyInterface_stub. + +Conclusion: +----------- + +Hopefully this document will get you well on your way into the world +of inter-process communication with KDE! Please direct all comments +and/or suggestions to Preston Brown <pbrown@kde.org> and Matthias +Ettrich <ettrich@kde.org>. + + +Inter-user communication +------------------------ + +Sometimes it might be interesting to use DCOP between processes +belonging to different users, e.g. a frontend process running +with the user's id, and a backend process running as root. + +To do this, two steps have to be taken: + +a) both processes need to talk to the same DCOP server +b) the authentication must be ensured + +For the first step, you simply pass the server address (as +found in .DCOPserver) to the second process. For the authentication, +you can use the ICEAUTHORITY environment variable to tell the +second process where to find the authentication information. +(Note that this implies that the second process is able to +read the authentication file, so it will probably only work +if the second process runs as root. If it should run as another +user, a similar approach to what kdesu does with xauth must +be taken. In fact, it would be a very good idea to add DCOP +support to kdesu!) + +For example + +ICEAUTHORITY=~user/.ICEauthority kdesu root -c kcmroot -dcopserver `cat ~user/.DCOPserver` + +will, after kdesu got the root password, execute kcmroot as root, talking +to the user's dcop server. + + +NOTE: DCOP communication is not encrypted, so please do not +pass important information around this way. + + +Performance Tests: +------------------ +A few back-of-the-napkin tests folks: + +Code: + +#include <kapplication.h> + +int main(int argc, char **argv) +{ + KApplication *app; + + app = new KApplication(argc, argv, "testit"); + return app->exec(); +} + +Compiled with: + +g++ -O2 -o testit testit.cpp -I$QTDIR/include -L$QTDIR/lib -lkdecore + +on Linux yields the following memory use statistics: + +VmSize: 8076 kB +VmLck: 0 kB +VmRSS: 4532 kB +VmData: 208 kB +VmStk: 20 kB +VmExe: 4 kB +VmLib: 6588 kB + +If I create the KApplication's DCOPClient, and call attach() and +registerAs(), it changes to this: + +VmSize: 8080 kB +VmLck: 0 kB +VmRSS: 4624 kB +VmData: 208 kB +VmStk: 20 kB +VmExe: 4 kB +VmLib: 6588 kB + +Basically it appears that using DCOP causes 100k more memory to be +resident, but no more data or stack. So this will be shared between all +processes, right? 100k to enable DCOP in all apps doesn't seem bad at +all. :) + +OK now for some timings. Just creating a KApplication and then exiting +(i.e. removing the call to KApplication::exec) takes this much time: + +0.28user 0.02system 0:00.32elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k +0inputs+0outputs (1084major+62minor)pagefaults 0swaps + +I.e. about 1/3 of a second on my PII-233. Now, if we create our DCOP +object and attach to the server, it takes this long: + +0.27user 0.03system 0:00.34elapsed 87%CPU (0avgtext+0avgdata 0maxresident)k +0inputs+0outputs (1107major+65minor)pagefaults 0swaps + +I.e. about 1/3 of a second. Basically DCOPClient creation and attaching +gets lost in the statistical variation ("noise"). I was getting times +between .32 and .48 over several runs for both of the example programs, so +obviously system load is more relevant than the extra two calls to +DCOPClient::attach and DCOPClient::registerAs, as well as the actual +DCOPClient constructor time. + diff --git a/server/Makefile.am b/server/Makefile.am new file mode 100644 index 00000000..33049961 --- /dev/null +++ b/server/Makefile.am @@ -0,0 +1,15 @@ + +INCLUDES=-I$(top_srcdir) $(DBUS_SERVER_CFLAGS) \ + -DDAEMON_NAME=\"dbus-daemon-1\" + +EFENCE= + +bin_PROGRAMS=dbus-daemon-1 + +dbus_daemon_1_SOURCES= \ + main.c + +dbus_daemon_1_LDADD= \ + $(EFENCE) \ + $(DBUS_SERVER_LIBS) \ + $(top_builddir)/dbus/libdbus-convenience.la diff --git a/server/main.c b/server/main.c new file mode 100644 index 00000000..1025a0e7 --- /dev/null +++ b/server/main.c @@ -0,0 +1,8 @@ + +int +main (int argc, char **argv) +{ + + + return 0; +} diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/Makefile.am |