diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /dcop/Mainpage.dox | |
download | tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'dcop/Mainpage.dox')
-rw-r--r-- | dcop/Mainpage.dox | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/dcop/Mainpage.dox b/dcop/Mainpage.dox new file mode 100644 index 000000000..af4f14ec3 --- /dev/null +++ b/dcop/Mainpage.dox @@ -0,0 +1,576 @@ +/* + A dummy source file for documenting the library. + Copied from HOWTO with small syntactic changes. +*/ + +/** + \mainpage The DCOP Desktop COmmunication Protocol library + +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. + +\section model 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 (also referred to as marshalling +in CORBA speak) 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. + +The manual method is covered first, followed by the automatic IDL method. + + +\section establish Establishing the Connection: + +KApplication has gained a method called \p 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 +\p DCOPClient::attach(). This will attempt to attach to the DCOP server. +If no server is found or there is any other type of error, +DCOPClient::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: + +\code +appId = client->registerAs(kapp->name()); +\endcode + +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 \p kapp->name(). You can retrieve the registered DCOP client +by calling \p kapp->dcopClient(). + + +\section sending_data 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 \p fooObject and has the child +\p barObject, you would reference this object as \p fooObject/barObject. +Functions must be described by a full function signature. If the +remote function is called \p doIt, and it takes an int, it would be +described as \p 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 \p 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 DCOPClient::send() indicates whether DCOP communication +succeeded or not. + +\code +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."); +\endcode + +OK, now let's say we wanted to get the data back from the remotely +called function. You have to execute a DCOPClient::call() instead of a +DCOPClient::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. + +\code +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!"); +} +\endcode + + +\section receiving_data 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. + +\code +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; + } +} +\endcode + + +\section receiving_calls 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: + +\code +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 ); +} +\endcode + + +\section dcopidl 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: + +\code +#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 +\endcode + +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: + +\code +class MyClass: public QObject, virtual public MyInterface +{ + Q_OBJECT + + public: + MyClass(); + ~MyClass(); + + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; +\endcode +\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 +practice, but it is essential here as you need to tell DCOPObject the name of +the interface which your are implementing. + +Example: + +\code +MyClass::MyClass() + : QObject(), + DCOPObject("MyInterface") +{ + // whatever... +} +\endcode + + +Now you can simply implement the methods you have declared in your interface, +exactly the same as you would normally. + +Example: + +\code +void MyClass::myAsynchronousMethod(QString someParameter) +{ + qDebug("myAsyncMethod called with param `" + someParameter + "'"); +} +\endcode + +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: + +\code +class MyClass: public QObject, virtual public DCOPObject +{ + Q_OBJECT + K_DCOP + + public: + MyClass(); + ~MyClass(); + + k_dcop: + ASYNC myAsynchronousMethod(QString someParameter); + QRect mySynchronousMethod(); +}; +\endcode + +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. + + +\section iuc 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. + +\section protocol DCOP Protocol description: + +A DCOPSend message does not expect any reply. +\code +data: << fromId << toId << objId << fun << dataSize + data[dataSize] +\endcode + +A DCOPCall message can get a DCOPReply, a DCOPReplyFailed +or a DCOPReplyWait message in response. +\code +data: << fromId << toId << objId << fun << dataSize + data[dataSize] +\endcode + +DCOPReply is the successful reply to a DCOPCall message +\code +data: << fromId << toId << replyType << replyDataSize + replyData[replyDataSize] +\endcode + +DCOPReplyFailed indicates failure of a DCOPCall message +\code +data: << fromId << toId +\endcode + +DCOPReplyWait indicates that a DCOPCall message is successfully +being processed but that response will come later. +\code +data: << fromId << toId << transactionId +\endcode + +DCOPReplyDelayed is the successful reply to a DCOPCall message +after a DCOPReplyWait message. +\code +data: << fromId << toId << transactionId << replyType << replyData +\endcode + +DCOPFind is a message much like a "call" message. It can however +be send to multiple objects within a client. If a function in a +object that is being called returns a boolean with the value "true", +a DCOPReply will be send back containing the DCOPRef of the object +who returned "true". + +All c-strings (fromId, toId, objId, fun and replyType), are marshalled with +their respective length as 32 bit unsigned integer first: +\code +data: length + string[length] +\endcode +\note This happens automatically when using QCString on a QDataStream. + +\section Deadlock protection and reentrancy + +When a DCOP call is made, the dcop client will be monitoring the +dcop connection for the reply on the call. When an incoming call is +received in this period, it will normally not be processed but queued +until the outgoing call has been fully handled. + +However, the above scenario would cause deadlock if the incoming call +was directly or indirectly a result of the outgoing call and the reply +on the outgoing call is waiting for the result of the incoming call. +(E.g. a circular call such as client A calling client B, with client B +calling client A) + +To prevent deadlock in this case, DCOP has a call tracing mechanism that +detects circular calls. When it detects an incoming circular call that +would otherwise be queued and as a result cause deadlock, it will handle +the incoming call immediately instead of queueing it. This means that the +incoming call may be processed at a point in the code where an outgoing +DCOP call is made. An application should be aware of this kind of +reentrancy. A special case of this is when a DCOP client makes a call +to itself, such calls are always handled directly. + +Call tracing works by appending a key to each outgoing call. When a client +receives an incoming call while waiting for a response on an outgoing call, +it will check if the key of the incoming call is equal to the key used for +the last outgoing call. If the keys are equal a circular call has been +detected. + +The key used by clients is 0 if they have not yet received any key. In this +case the server will send them back a unique key that they should use in +further calls. If a client makes an outgoing call in response to an incoming +call it will use the key of the incoming call for the outgoing call instead +of the key that was received from the server. + +A key value of 1 has a special meaning and is used for non-call messages +such as DCOPSend, DCOPReplyFailed and DCOP signals. + +A key value of 2 has a special meaning and is used for priority calls. +When a dcop clien is in priority call mode, it will only handle incoming +calls that have a key value of 2. + +NOTE: If client A and client B would call each other simultaneously there +is still a risk of deadlock because both calls would have unique keys and +both clients would decide to queue the incoming call until they receive +a response on their outgoing call. + +\section dcop_signals 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. For declaration of dcop signals, the keyword +\p k_dcop_signals is provided. A declaration looks like this: + +\code +class Example : virtual public DCOPClient +{ + K_DCOP + + k_dcop: + // some ordinary dcop methods here + ... + k_dcop_signals: + // our dcop signal + void clientDied(pid_t pid); + ... +} +\endcode + +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). + +\note 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: + +\code +QByteArray params; +QDataStream stream(params, IO_WriteOnly); +stream << pid; +kapp->dcopClient()->emitDCOPSignal("clientDied(pid_t)", params); +\endcode + +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: + +\code +connectDCOPSignal(0, 0, "clientDied(pid_t)", "clientDied(pid_t)", false); +\endcode + +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. + + +\section conclusion 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 the KDE Core Developers List \<kde-core-devel@kde.org\>. + +*/ |