src/gui/embedded/qcopchannel_qws.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    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 
       
    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 
       
    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 
       
    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 
       
   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 */
       
   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 
       
   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   */
       
   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 
       
   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 
       
   254 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  */
       
   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 
       
   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 
       
   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 
       
   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 */
       
   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 
       
   408 void QWSServerSignalBridge::emitNewChannel(const QString& channel){
       
   409   emit newChannel(channel);
       
   410 }
       
   411 
       
   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 
       
   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 
       
   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 
       
   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" : "unkown");
       
   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 */
       
   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