summaryrefslogtreecommitdiffstats
path: root/dcop/Mainpage.dox
diff options
context:
space:
mode:
Diffstat (limited to 'dcop/Mainpage.dox')
-rw-r--r--dcop/Mainpage.dox576
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\>.
+
+*/