src/declarative/debugger/qdeclarativedebugservice.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/declarative/debugger/qdeclarativedebugservice.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qdeclarativedebugservice_p.h"
+
+#include "private/qpacketprotocol_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtCore/qstringlist.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeDebugServerPrivate;
+class QDeclarativeDebugServer : public QObject
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(QDeclarativeDebugServer)
+    Q_DISABLE_COPY(QDeclarativeDebugServer)
+public:
+    static QDeclarativeDebugServer *instance();
+    void listen();
+    bool hasDebuggingClient() const;
+
+private Q_SLOTS:
+    void readyRead();
+    void newConnection();
+
+private:
+    friend class QDeclarativeDebugService;
+    friend class QDeclarativeDebugServicePrivate;
+    QDeclarativeDebugServer(int); 
+};
+
+class QDeclarativeDebugServerPrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QDeclarativeDebugServer)
+public:
+    QDeclarativeDebugServerPrivate();
+
+    int port;
+    QTcpSocket *connection;
+    QPacketProtocol *protocol;
+    QHash<QString, QDeclarativeDebugService *> plugins;
+    QStringList enabledPlugins;
+    QTcpServer *tcpServer;
+    bool gotHello;
+};
+
+class QDeclarativeDebugServicePrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QDeclarativeDebugService)
+public:
+    QDeclarativeDebugServicePrivate();
+
+    QString name;
+    QDeclarativeDebugServer *server;
+};
+
+QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate()
+: connection(0), protocol(0), gotHello(false)
+{
+}
+
+void QDeclarativeDebugServer::listen()
+{
+    Q_D(QDeclarativeDebugServer);
+
+    d->tcpServer = new QTcpServer(this);
+    QObject::connect(d->tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
+    if (d->tcpServer->listen(QHostAddress::Any, d->port)) 
+        qWarning("QDeclarativeDebugServer: Waiting for connection on port %d...", d->port);
+    else
+        qWarning("QDeclarativeDebugServer: Unable to listen on port %d", d->port);
+}
+
+void QDeclarativeDebugServer::newConnection()
+{
+    Q_D(QDeclarativeDebugServer);
+
+    if (d->connection) {
+        qWarning("QDeclarativeDebugServer error: another client is already connected");
+        return;
+    }
+
+    d->connection = d->tcpServer->nextPendingConnection();
+    d->connection->setParent(this);
+    d->protocol = new QPacketProtocol(d->connection, this);
+    QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
+}
+
+bool QDeclarativeDebugServer::hasDebuggingClient() const
+{
+    Q_D(const QDeclarativeDebugServer);
+    return d->gotHello;
+}
+
+QDeclarativeDebugServer *QDeclarativeDebugServer::instance()
+{
+    static bool envTested = false;
+    static QDeclarativeDebugServer *server = 0;
+
+    if (!envTested) {
+        envTested = true;
+        QByteArray env = qgetenv("QML_DEBUG_SERVER_PORT");
+
+        bool ok = false;
+        int port = env.toInt(&ok);
+
+        if (ok && port > 1024) {
+            server = new QDeclarativeDebugServer(port);
+            server->listen();
+        }
+    }
+
+    return server;
+}
+
+QDeclarativeDebugServer::QDeclarativeDebugServer(int port)
+: QObject(*(new QDeclarativeDebugServerPrivate))
+{
+    Q_D(QDeclarativeDebugServer);
+    d->port = port;
+}
+
+void QDeclarativeDebugServer::readyRead()
+{
+    Q_D(QDeclarativeDebugServer);
+
+    if (!d->gotHello) {
+        QPacket hello = d->protocol->read();
+        QString name; 
+        hello >> name >> d->enabledPlugins;
+        if (name != QLatin1String("QDeclarativeDebugServer")) {
+            qWarning("QDeclarativeDebugServer: Invalid hello message");
+            QObject::disconnect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
+            d->protocol->deleteLater();
+            d->protocol = 0;
+            d->connection->deleteLater();
+            d->connection = 0;
+            return;
+        }
+        d->gotHello = true;
+        qWarning("QDeclarativeDebugServer: Connection established");
+    }
+
+    QString debugServer(QLatin1String("QDeclarativeDebugServer"));
+
+    while (d->protocol->packetsAvailable()) {
+        QPacket pack = d->protocol->read();
+
+        QString name;
+        pack >> name;
+
+        if (name == debugServer) {
+            int op = -1; QString plugin;
+            pack >> op >> plugin;
+
+            if (op == 1) {
+                // Enable
+                if (!d->enabledPlugins.contains(plugin)) {
+                    d->enabledPlugins.append(plugin);
+                    QHash<QString, QDeclarativeDebugService *>::Iterator iter = 
+                        d->plugins.find(plugin);
+                    if (iter != d->plugins.end())
+                        (*iter)->enabledChanged(true);
+                }
+
+            } else if (op == 2) {
+                // Disable
+                if (d->enabledPlugins.contains(plugin)) {
+                    d->enabledPlugins.removeAll(plugin);
+                    QHash<QString, QDeclarativeDebugService *>::Iterator iter = 
+                        d->plugins.find(plugin);
+                    if (iter != d->plugins.end())
+                        (*iter)->enabledChanged(false);
+                }
+            } else {
+                qWarning("QDeclarativeDebugServer: Invalid control message %d", op);
+            }
+
+        } else {
+            QByteArray message;
+            pack >> message;
+
+            QHash<QString, QDeclarativeDebugService *>::Iterator iter = 
+                d->plugins.find(name);
+            if (iter == d->plugins.end()) {
+                qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << name;
+            } else {
+                (*iter)->messageReceived(message);
+            }
+        }
+    }
+}
+
+QDeclarativeDebugServicePrivate::QDeclarativeDebugServicePrivate()
+: server(0)
+{
+}
+
+QDeclarativeDebugService::QDeclarativeDebugService(const QString &name, QObject *parent)
+: QObject(*(new QDeclarativeDebugServicePrivate), parent)
+{
+    Q_D(QDeclarativeDebugService);
+    d->name = name;
+    d->server = QDeclarativeDebugServer::instance();
+
+    if (!d->server)
+        return;
+
+    if (d->server->d_func()->plugins.contains(name)) {
+        qWarning() << "QDeclarativeDebugService: Conflicting plugin name" << name;
+        d->server = 0;
+    } else {
+        d->server->d_func()->plugins.insert(name, this);
+    }
+}
+
+QString QDeclarativeDebugService::name() const
+{
+    Q_D(const QDeclarativeDebugService);
+    return d->name;
+}
+
+bool QDeclarativeDebugService::isEnabled() const
+{
+    Q_D(const QDeclarativeDebugService);
+    return (d->server && d->server->d_func()->enabledPlugins.contains(d->name));
+}
+
+namespace {
+
+    struct ObjectReference 
+    {
+        QPointer<QObject> object;
+        int id;
+    };
+
+    struct ObjectReferenceHash 
+    {
+        ObjectReferenceHash() : nextId(0) {}
+
+        QHash<QObject *, ObjectReference> objects;
+        QHash<int, QObject *> ids;
+
+        int nextId;
+    };
+
+}
+Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash);
+
+
+/*!
+    Returns a unique id for \a object.  Calling this method multiple times
+    for the same object will return the same id.
+*/
+int QDeclarativeDebugService::idForObject(QObject *object)
+{
+    if (!object)
+        return -1;
+
+    ObjectReferenceHash *hash = objectReferenceHash();
+    QHash<QObject *, ObjectReference>::Iterator iter = 
+        hash->objects.find(object);
+
+    if (iter == hash->objects.end()) {
+        int id = hash->nextId++;
+
+        hash->ids.insert(id, object);
+        iter = hash->objects.insert(object, ObjectReference());
+        iter->object = object;
+        iter->id = id;
+    } else if (iter->object != object) {
+        int id = hash->nextId++;
+
+        hash->ids.remove(iter->id);
+
+        hash->ids.insert(id, object);
+        iter->object = object;
+        iter->id = id;
+    } 
+    return iter->id;
+}
+
+/*!
+    Returns the object for unique \a id.  If the object has not previously been
+    assigned an id, through idForObject(), then 0 is returned.  If the object
+    has been destroyed, 0 is returned.
+*/
+QObject *QDeclarativeDebugService::objectForId(int id)
+{
+    ObjectReferenceHash *hash = objectReferenceHash();
+
+    QHash<int, QObject *>::Iterator iter = hash->ids.find(id);
+    if (iter == hash->ids.end())
+        return 0;
+
+
+    QHash<QObject *, ObjectReference>::Iterator objIter = 
+        hash->objects.find(*iter);
+    Q_ASSERT(objIter != hash->objects.end());
+
+    if (objIter->object == 0) {
+        hash->ids.erase(iter);
+        hash->objects.erase(objIter);
+        return 0;
+    } else {
+        return *iter;
+    }
+}
+
+bool QDeclarativeDebugService::isDebuggingEnabled()
+{
+    return QDeclarativeDebugServer::instance() != 0;
+}
+
+bool QDeclarativeDebugService::hasDebuggingClient()
+{
+    return QDeclarativeDebugServer::instance() != 0
+            && QDeclarativeDebugServer::instance()->hasDebuggingClient();
+}
+
+QString QDeclarativeDebugService::objectToString(QObject *obj)
+{
+    if(!obj)
+        return QLatin1String("NULL");
+
+    QString objectName = obj->objectName();
+    if(objectName.isEmpty())
+        objectName = QLatin1String("<unnamed>");
+
+    QString rv = QString::fromUtf8(obj->metaObject()->className()) + 
+                 QLatin1String(": ") + objectName;
+
+    return rv;
+}
+
+void QDeclarativeDebugService::sendMessage(const QByteArray &message)
+{
+    Q_D(QDeclarativeDebugService);
+
+    if (!d->server || !d->server->d_func()->connection)
+        return;
+
+    QPacket pack;
+    pack << d->name << message;
+    d->server->d_func()->protocol->send(pack);
+    d->server->d_func()->connection->flush();
+}
+
+void QDeclarativeDebugService::enabledChanged(bool)
+{
+}
+
+void QDeclarativeDebugService::messageReceived(const QByteArray &)
+{
+}
+
+QT_END_NAMESPACE
+
+#include <qdeclarativedebugservice.moc>