1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
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
58 QT_BEGIN_NAMESPACE
59
60 typedef QMap<QString, QList<QWSClient*> > QCopServerMap;
61 static QCopServerMap *qcopServerMap = 0;
62
63 class QCopServerRegexp
64 {
65 public:
66 QCopServerRegexp( const QString& channel, QWSClient *client );
67 QCopServerRegexp( const QCopServerRegexp& other );
68
69 QString channel;
70 QWSClient *client;
71 QRegExp regexp;
72 };
73
QCopServerRegexp(const QString & channel,QWSClient * client)74 QCopServerRegexp::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
QCopServerRegexp(const QCopServerRegexp & other)81 QCopServerRegexp::QCopServerRegexp( const QCopServerRegexp& other )
82 {
83 channel = other.channel;
84 client = other.client;
85 regexp = other.regexp;
86 }
87
88 typedef QList<QCopServerRegexp> QCopServerRegexpList;
89 static QCopServerRegexpList *qcopServerRegexpList = 0;
90
91 typedef QMap<QString, QList< QPointer<QCopChannel> > > QCopClientMap;
92 static QCopClientMap *qcopClientMap = 0;
93
Q_GLOBAL_STATIC(QMutex,qcopClientMapMutex)94 Q_GLOBAL_STATIC(QMutex, qcopClientMapMutex)
95
96 // Determine if a channel name contains wildcard characters.
97 static bool containsWildcards( const QString& channel )
98 {
99 return channel.contains(QLatin1Char('*'));
100 }
101
102 class QCopChannelPrivate
103 {
104 public:
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
QCopChannel(const QString & channel,QObject * parent)154 QCopChannel::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 */
QCopChannel(const QString & channel,QObject * parent,const char * name)165 QCopChannel::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
init(const QString & channel)173 void 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 */
reregisterAll()210 void 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
~QCopChannel()229 QCopChannel::~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
channel() const254 QString 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 */
receive(const QString & msg,const QByteArray & data)283 void 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
isRegistered(const QString & channel)305 bool 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
send(const QString & channel,const QString & msg)324 bool 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
send(const QString & channel,const QString & msg,const QByteArray & data)358 bool 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 */
flush()383 bool 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
396 class QWSServerSignalBridge : public QObject {
397 Q_OBJECT
398
399 public:
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
emitNewChannel(const QString & channel)408 void QWSServerSignalBridge::emitNewChannel(const QString& channel){
409 emit newChannel(channel);
410 }
411
emitRemovedChannel(const QString & channel)412 void 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
registerChannel(const QString & ch,QWSClient * cl)421 void 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
detach(QWSClient * cl)456 void 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
answer(QWSClient * cl,const QString & ch,const QString & msg,const QByteArray & data)493 void 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 */
sendLocally(const QString & ch,const QString & msg,const QByteArray & data)582 void 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
604 QT_END_NAMESPACE
605
606 #include "qcopchannel_qws.moc"
607
608 #endif
609