source: trunk/src/gui/embedded/qcopchannel_qws.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 18.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qcopchannel_qws.h"
43
44#ifndef QT_NO_COP
45
46#include "qwsdisplay_qws.h"
47#include "qwscommand_qws_p.h"
48#include "qwindowsystem_qws.h"
49#include "qwindowsystem_p.h"
50#include "qlist.h"
51#include "qmap.h"
52#include "qdatastream.h"
53#include "qpointer.h"
54#include "qmutex.h"
55
56#include "qdebug.h"
57
58QT_BEGIN_NAMESPACE
59
60typedef QMap<QString, QList<QWSClient*> > QCopServerMap;
61static QCopServerMap *qcopServerMap = 0;
62
63class QCopServerRegexp
64{
65public:
66 QCopServerRegexp( const QString& channel, QWSClient *client );
67 QCopServerRegexp( const QCopServerRegexp& other );
68
69 QString channel;
70 QWSClient *client;
71 QRegExp regexp;
72};
73
74QCopServerRegexp::QCopServerRegexp( const QString& channel, QWSClient *client )
75{
76 this->channel = channel;
77 this->client = client;
78 this->regexp = QRegExp( channel, Qt::CaseSensitive, QRegExp::Wildcard );
79}
80
81QCopServerRegexp::QCopServerRegexp( const QCopServerRegexp& other )
82{
83 channel = other.channel;
84 client = other.client;
85 regexp = other.regexp;
86}
87
88typedef QList<QCopServerRegexp> QCopServerRegexpList;
89static QCopServerRegexpList *qcopServerRegexpList = 0;
90
91typedef QMap<QString, QList< QPointer<QCopChannel> > > QCopClientMap;
92static QCopClientMap *qcopClientMap = 0;
93
94Q_GLOBAL_STATIC(QMutex, qcopClientMapMutex)
95
96// Determine if a channel name contains wildcard characters.
97static bool containsWildcards( const QString& channel )
98{
99 return channel.contains(QLatin1Char('*'));
100}
101
102class QCopChannelPrivate
103{
104public:
105 QString channel;
106};
107
108/*!
109 \class QCopChannel
110 \ingroup qws
111
112 \brief The QCopChannel class provides communication capabilities
113 between clients in \l{Qt for Embedded Linux}.
114
115 Note that this class is only available in \l{Qt for Embedded Linux}.
116
117 The Qt COmmunication Protocol (QCOP) is a many-to-many protocol
118 for transferring messages across registered channels. A channel is
119 registered by name, and anyone who wants to can listen to the
120 channel as well as send messages through it. The QCOP protocol
121 allows clients to communicate both within the same address space
122 and between different processes.
123
124 To send messages to a given channel, QCopChannel provides the
125 static send() function. Using this function alone, the messages
126 are queued until Qt re-enters the event loop. To immediately flush
127 all queued messages to the registered listeners, call the static
128 flush() function.
129
130 To listen to the traffic on a given channel, you typically
131 instantiate a QCopChannel object for the given channel and connect
132 to its received() signal that is emitted whenever there is
133 incoming data. Use the static isRegistered() function to query
134 the server for the existence of a given channel. QCopChannel
135 provides the channel() function returning the name of this
136 QCopChannel object's channel.
137
138 In additon, QCopChannel provides the virtual receive() function
139 that can be reimplemented to filter the incoming messages and
140 data. The default implementation simply emits the received()
141 signal.
142
143 \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture}
144*/
145
146/*!
147 Constructs a QCopChannel object for the specified \a channel, with
148 the given \a parent. Once created, the channel is registered by
149 the server.
150
151 \sa isRegistered(), channel()
152*/
153
154QCopChannel::QCopChannel(const QString& channel, QObject *parent) :
155 QObject(parent)
156{
157 init(channel);
158}
159
160#ifdef QT3_SUPPORT
161/*!
162 Use the two argument overload instead, and call the
163 QObject::setObjectName() function to \a name the instance.
164*/
165QCopChannel::QCopChannel(const QString& channel, QObject *parent, const char *name) :
166 QObject(parent)
167{
168 setObjectName(QString::fromAscii(name));
169 init(channel);
170}
171#endif
172
173void QCopChannel::init(const QString& channel)
174{
175 d = new QCopChannelPrivate;
176 d->channel = channel;
177
178 if (!qt_fbdpy) {
179 qFatal("QCopChannel: Must construct a QApplication "
180 "before QCopChannel");
181 return;
182 }
183
184 {
185 QMutexLocker locker(qcopClientMapMutex());
186
187 if (!qcopClientMap)
188 qcopClientMap = new QCopClientMap;
189
190 // do we need a new channel list ?
191 QCopClientMap::Iterator it = qcopClientMap->find(channel);
192 if (it != qcopClientMap->end()) {
193 it.value().append(this);
194 return;
195 }
196
197 it = qcopClientMap->insert(channel, QList< QPointer<QCopChannel> >());
198 it.value().append(QPointer<QCopChannel>(this));
199 }
200
201 // inform server about this channel
202 qt_fbdpy->registerChannel(channel);
203}
204
205/*!
206 \internal
207
208 Resend all channel registrations
209 */
210void QCopChannel::reregisterAll()
211{
212 if(qcopClientMap)
213 for(QCopClientMap::Iterator iter = qcopClientMap->begin();
214 iter != qcopClientMap->end();
215 ++iter)
216 qt_fbdpy->registerChannel(iter.key());
217}
218
219/*!
220 Destroys this QCopChannel object.
221
222 The server is notified that this particular listener has closed
223 its connection. The server will keep the channel open until the
224 last registered listener detaches.
225
226 \sa isRegistered(), channel()
227*/
228
229QCopChannel::~QCopChannel()
230{
231 QMutexLocker locker(qcopClientMapMutex());
232 QCopClientMap::Iterator it = qcopClientMap->find(d->channel);
233 Q_ASSERT(it != qcopClientMap->end());
234 it.value().removeAll(this);
235 // still any clients connected locally ?
236 if (it.value().isEmpty()) {
237 QByteArray data;
238 QDataStream s(&data, QIODevice::WriteOnly);
239 s << d->channel;
240 if (qt_fbdpy)
241 send(QLatin1String(""), QLatin1String("detach()"), data);
242 qcopClientMap->remove(d->channel);
243 }
244
245 delete d;
246}
247
248/*!
249 Returns the name of this object's channel.
250
251 \sa isRegistered()
252*/
253
254QString QCopChannel::channel() const
255{
256 return d->channel;
257}
258
259/*!
260 \fn void QCopChannel::receive(const QString& message, const QByteArray &data)
261
262 Processes the incoming \a message and \a data.
263
264 This function is called by the server when this object's channel
265 receives new messages. Note that the default implementation simply
266 emits the received() signal; reimplement this function to process
267 the incoming \a message and \a data.
268
269 Note that the format of the given \a data has to be well defined
270 in order to extract the information it contains. In addition, it
271 is recommended to use the DCOP convention. This is not a
272 requirement, but you must ensure that the sender and receiver
273 agree on the argument types. For example:
274
275 \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 0
276
277 The above code assumes that the \c message is a DCOP-style
278 function signature and the \c data contains the function's
279 arguments.
280
281 \sa send(), channel(), received()
282 */
283void QCopChannel::receive(const QString& msg, const QByteArray &data)
284{
285 emit received(msg, data);
286}
287
288/*!
289 \fn void QCopChannel::received(const QString& message, const QByteArray &data)
290
291 This signal is emitted whenever this object's channel receives new
292 messages (i.e., it is emitted by the receive() function), passing
293 the incoming \a message and \a data as parameters.
294
295 \sa receive(), channel()
296*/
297
298/*!
299 Queries the server for the existence of the given \a channel. Returns true
300 if the channel is registered; otherwise returns false.
301
302 \sa channel(), send()
303*/
304
305bool QCopChannel::isRegistered(const QString& channel)
306{
307 QByteArray data;
308 QDataStream s(&data, QIODevice::WriteOnly);
309 s << channel;
310 if (!send(QLatin1String(""), QLatin1String("isRegistered()"), data))
311 return false;
312
313 QWSQCopMessageEvent *e = qt_fbdpy->waitForQCopResponse();
314 bool known = e->message == "known";
315 delete e;
316 return known;
317}
318
319/*!
320 \fn bool QCopChannel::send(const QString& channel, const QString& message)
321 \overload
322*/
323
324bool QCopChannel::send(const QString& channel, const QString& msg)
325{
326 QByteArray data;
327 return send(channel, msg, data);
328}
329
330/*!
331 \fn bool QCopChannel::send(const QString& channel, const QString& message,
332 const QByteArray &data)
333
334 Sends the given \a message on the specified \a channel with the
335 given \a data. The message will be distributed to all clients
336 subscribed to the channel. Returns true if the message is sent
337 successfully; otherwise returns false.
338
339 It is recommended to use the DCOP convention. This is not a
340 requirement, but you must ensure that the sender and receiver
341 agree on the argument types.
342
343 Note that QDataStream provides a convenient way to fill the byte
344 array with auxiliary data. For example:
345
346 \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 1
347
348 In the code above the channel is \c "System/Shell". The \c message
349 is an arbitrary string, but in the example we've used the DCOP
350 convention of passing a function signature. Such a signature is
351 formatted as \c "functionname(types)" where \c types is a list of
352 zero or more comma-separated type names, with no whitespace, no
353 consts and no pointer or reference marks, i.e. no "*" or "&".
354
355 \sa receive(), isRegistered()
356*/
357
358bool QCopChannel::send(const QString& channel, const QString& msg,
359 const QByteArray &data)
360{
361 if (!qt_fbdpy) {
362 qFatal("QCopChannel::send: Must construct a QApplication "
363 "before using QCopChannel");
364 return false;
365 }
366
367 qt_fbdpy->sendMessage(channel, msg, data);
368
369 return true;
370}
371
372/*!
373 \since 4.2
374
375 Flushes all queued messages to the registered listeners.
376
377 Note that this function returns false if no QApplication has been
378 constructed, otherwise it returns true.
379
380 \sa send()
381
382*/
383bool QCopChannel::flush()
384{
385 if (!qt_fbdpy) {
386 qFatal("QCopChannel::flush: Must construct a QApplication "
387 "before using QCopChannel");
388 return false;
389 }
390
391 qt_fbdpy->flushCommands();
392
393 return true;
394}
395
396class QWSServerSignalBridge : public QObject {
397 Q_OBJECT
398
399public:
400 void emitNewChannel(const QString& channel);
401 void emitRemovedChannel(const QString& channel);
402
403 signals:
404 void newChannel(const QString& channel);
405 void removedChannel(const QString& channel);
406};
407
408void QWSServerSignalBridge::emitNewChannel(const QString& channel){
409 emit newChannel(channel);
410}
411
412void QWSServerSignalBridge::emitRemovedChannel(const QString& channel) {
413 emit removedChannel(channel);
414}
415
416/*!
417 \internal
418 Server side: subscribe client \a cl on channel \a ch.
419*/
420
421void QCopChannel::registerChannel(const QString& ch, QWSClient *cl)
422{
423 if (!qcopServerMap)
424 qcopServerMap = new QCopServerMap;
425
426 // do we need a new channel list ?
427 QCopServerMap::Iterator it = qcopServerMap->find(ch);
428 if (it == qcopServerMap->end())
429 it = qcopServerMap->insert(ch, QList<QWSClient*>());
430
431 // If the channel name contains wildcard characters, then we also
432 // register it on the server regexp matching list.
433 if (containsWildcards( ch )) {
434 QCopServerRegexp item(ch, cl);
435 if (!qcopServerRegexpList)
436 qcopServerRegexpList = new QCopServerRegexpList;
437 qcopServerRegexpList->append( item );
438 }
439
440 // If this is the first client in the channel, announce the channel as being created.
441 if (it.value().count() == 0) {
442 QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
443 connect(qwsBridge, SIGNAL(newChannel(QString)), qwsServer, SIGNAL(newChannel(QString)));
444 qwsBridge->emitNewChannel(ch);
445 delete qwsBridge;
446 }
447
448 it.value().append(cl);
449}
450
451/*!
452 \internal
453 Server side: unsubscribe \a cl from all channels.
454*/
455
456void QCopChannel::detach(QWSClient *cl)
457{
458 if (!qcopServerMap)
459 return;
460
461 QCopServerMap::Iterator it = qcopServerMap->begin();
462 for (; it != qcopServerMap->end(); ++it) {
463 if (it.value().contains(cl)) {
464 it.value().removeAll(cl);
465 // If this was the last client in the channel, announce the channel as dead.
466 if (it.value().count() == 0) {
467 QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
468 connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
469 qwsBridge->emitRemovedChannel(it.key());
470 delete qwsBridge;
471 }
472 }
473 }
474
475 if (!qcopServerRegexpList)
476 return;
477
478 QCopServerRegexpList::Iterator it2 = qcopServerRegexpList->begin();
479 while(it2 != qcopServerRegexpList->end()) {
480 if ((*it2).client == cl)
481 it2 = qcopServerRegexpList->erase(it2);
482 else
483 ++it2;
484 }
485}
486
487/*!
488 \internal
489 Server side: transmit the message to all clients registered to the
490 specified channel.
491*/
492
493void QCopChannel::answer(QWSClient *cl, const QString& ch,
494 const QString& msg, const QByteArray &data)
495{
496 // internal commands
497 if (ch.isEmpty()) {
498 if (msg == QLatin1String("isRegistered()")) {
499 QString c;
500 QDataStream s(data);
501 s >> c;
502 bool known = qcopServerMap && qcopServerMap->contains(c)
503 && !((*qcopServerMap)[c]).isEmpty();
504 // Yes, it's a typo, it's not user-visible, and we choose not to fix it for compatibility
505 QLatin1String ans = QLatin1String(known ? "known" : "unknown");
506 QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
507 ans, data, true);
508 return;
509 } else if (msg == QLatin1String("detach()")) {
510 QString c;
511 QDataStream s(data);
512 s >> c;
513 Q_ASSERT(qcopServerMap);
514 QCopServerMap::Iterator it = qcopServerMap->find(c);
515 if (it != qcopServerMap->end()) {
516 //Q_ASSERT(it.value().contains(cl));
517 it.value().removeAll(cl);
518 if (it.value().isEmpty()) {
519 // If this was the last client in the channel, announce the channel as dead
520 QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
521 connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
522 qwsBridge->emitRemovedChannel(it.key());
523 delete qwsBridge;
524 qcopServerMap->erase(it);
525 }
526 }
527 if (qcopServerRegexpList && containsWildcards(c)) {
528 // Remove references to a wildcarded channel.
529 QCopServerRegexpList::Iterator it
530 = qcopServerRegexpList->begin();
531 while(it != qcopServerRegexpList->end()) {
532 if ((*it).client == cl && (*it).channel == c)
533 it = qcopServerRegexpList->erase(it);
534 else
535 ++it;
536 }
537 }
538 return;
539 }
540 qWarning("QCopChannel: unknown internal command %s", qPrintable(msg));
541 QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
542 QLatin1String("bad"), data);
543 return;
544 }
545
546 if (qcopServerMap) {
547 QList<QWSClient*> clist = qcopServerMap->value(ch);
548 for (int i=0; i < clist.size(); ++i) {
549 QWSClient *c = clist.at(i);
550 QWSServerPrivate::sendQCopEvent(c, ch, msg, data);
551 }
552 }
553
554 if(qcopServerRegexpList && !containsWildcards(ch)) {
555 // Search for wildcard matches and forward the message on.
556 QCopServerRegexpList::ConstIterator it = qcopServerRegexpList->constBegin();
557 for (; it != qcopServerRegexpList->constEnd(); ++it) {
558 if ((*it).regexp.exactMatch(ch)) {
559 QByteArray newData;
560 {
561 QDataStream stream
562 (&newData, QIODevice::WriteOnly | QIODevice::Append);
563 stream << ch;
564 stream << msg;
565 stream << data;
566 // Stream is flushed and closed at this point.
567 }
568 QWSServerPrivate::sendQCopEvent
569 ((*it).client, (*it).channel,
570 QLatin1String("forwardedMessage(QString,QString,QByteArray)"),
571 newData);
572 }
573 }
574 }
575}
576
577/*!
578 \internal
579 Client side: distribute received event to the QCop instance managing the
580 channel.
581*/
582void QCopChannel::sendLocally(const QString& ch, const QString& msg,
583 const QByteArray &data)
584{
585 Q_ASSERT(qcopClientMap);
586
587 // filter out internal events
588 if (ch.isEmpty())
589 return;
590
591 // feed local clients with received data
592 QList< QPointer<QCopChannel> > clients;
593 {
594 QMutexLocker locker(qcopClientMapMutex());
595 clients = (*qcopClientMap)[ch];
596 }
597 for (int i = 0; i < clients.size(); ++i) {
598 QCopChannel *channel = (QCopChannel *)clients.at(i);
599 if ( channel )
600 channel->receive(msg, data);
601 }
602}
603
604QT_END_NAMESPACE
605
606#include "qcopchannel_qws.moc"
607
608#endif
Note: See TracBrowser for help on using the repository browser.