nodeinstanceserverproxy.cpp 19.3 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18 19
**
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28 29 30
**
**************************************************************************/

31 32 33 34 35 36 37
#include "nodeinstanceserverproxy.h"

#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
#include <QCoreApplication>
#include <QUuid>
38
#include <QFileInfo>
39 40 41 42 43 44 45 46 47

#include "propertyabstractcontainer.h"
#include "propertyvaluecontainer.h"
#include "propertybindingcontainer.h"
#include "instancecontainer.h"
#include "createinstancescommand.h"
#include "createscenecommand.h"
#include "changevaluescommand.h"
#include "changebindingscommand.h"
48
#include "changeauxiliarycommand.h"
49 50 51 52 53 54 55
#include "changefileurlcommand.h"
#include "removeinstancescommand.h"
#include "clearscenecommand.h"
#include "removepropertiescommand.h"
#include "reparentinstancescommand.h"
#include "changeidscommand.h"
#include "changestatecommand.h"
56
#include "completecomponentcommand.h"
57
#include "changenodesourcecommand.h"
58 59 60 61

#include "informationchangedcommand.h"
#include "pixmapchangedcommand.h"
#include "valueschangedcommand.h"
62
#include "childrenchangedcommand.h"
63 64
#include "imagecontainer.h"
#include "statepreviewimagechangedcommand.h"
65
#include "componentcompletedcommand.h"
66
#include "tokencommand.h"
67
#include "removesharedmemorycommand.h"
68

69 70
#include "synchronizecommand.h"

71
#include "nodeinstanceview.h"
72 73

#include "import.h"
74 75 76

#include <utils/hostosinfo.h>

77 78
#include <QMessageBox>

79 80 81

namespace QmlDesigner {

82
static bool hasQtQuick2(NodeInstanceView *nodeInstanceView)
83 84 85
{
    if (nodeInstanceView && nodeInstanceView->model()) {
        foreach (const Import &import ,nodeInstanceView->model()->imports()) {
86
            if (import.url() ==  "QtQuick" && import.version().toDouble() >= 2.0)
87 88 89 90 91 92 93
                return true;
        }
    }

    return false;
}

94
NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceView, RunModus runModus, const QString &pathToQt)
95 96 97
    : NodeInstanceServerInterface(nodeInstanceView),
      m_localServer(new QLocalServer(this)),
      m_nodeInstanceView(nodeInstanceView),
98
      m_firstBlockSize(0),
99
      m_secondBlockSize(0),
100
      m_thirdBlockSize(0),
101 102 103 104
      m_writeCommandCounter(0),
      m_firstLastReadCommandCounter(0),
      m_secondLastReadCommandCounter(0),
      m_thirdLastReadCommandCounter(0),
105 106
      m_runModus(runModus),
      m_synchronizeId(-1)
107
{
108
   Q_UNUSED(pathToQt);
109 110 111
   QString socketToken(QUuid::createUuid().toString());

   m_localServer->listen(socketToken);
112
   m_localServer->setMaxPendingConnections(3);
113

114 115
   QString applicationPath =  pathToQt + QLatin1String("/bin");
   if (runModus == TestModus) {
116 117 118
       applicationPath = QCoreApplication::applicationDirPath()
               + QLatin1String("/../../../../../bin/")
               + qmlPuppetApplicationName();
119
   } else {
120
       applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
121
       applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
122 123 124 125
       if (!QFileInfo(applicationPath).exists()) { //No qmlpuppet in Qt
           //We have to find out how to give not too intrusive feedback
           applicationPath =  QCoreApplication::applicationDirPath();
           applicationPath = macOSBundlePath(applicationPath);
Robert Loehning's avatar
Robert Loehning committed
126
           applicationPath += QLatin1Char('/') + qmlPuppetApplicationName();
127
       }
128
   }
129

130 131 132 133 134
   QByteArray envImportPath = qgetenv("QTCREATOR_QMLPUPPET_PATH");
   if (!envImportPath.isEmpty()) {
       applicationPath = envImportPath;
   }

135 136 137
   QProcessEnvironment enviroment = QProcessEnvironment::systemEnvironment();
   enviroment.insert("QML_NO_THREADED_RENDERER", "true");

138 139
   if (QFileInfo(applicationPath).exists()) {
       m_qmlPuppetEditorProcess = new QProcess;
140 141
       m_qmlPuppetEditorProcess->setProcessEnvironment(enviroment);
       m_qmlPuppetEditorProcess->setObjectName("EditorProcess");
142 143 144
       connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetEditorProcess.data(), SLOT(kill()));
       bool fowardQmlpuppetOutput = !qgetenv("FORWARD_QMLPUPPET_OUTPUT").isEmpty();
145
       if (fowardQmlpuppetOutput)
146 147
           m_qmlPuppetEditorProcess->setProcessChannelMode(QProcess::ForwardedChannels);
       m_qmlPuppetEditorProcess->start(applicationPath, QStringList() << socketToken << "editormode" << "-graphicssystem raster");
148

149 150
       if (runModus == NormalModus) {
           m_qmlPuppetPreviewProcess = new QProcess;
151 152
           m_qmlPuppetPreviewProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetPreviewProcess->setObjectName("PreviewProcess");
153 154 155 156 157
           connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetPreviewProcess.data(), SLOT(kill()));
           if (fowardQmlpuppetOutput)
               m_qmlPuppetPreviewProcess->setProcessChannelMode(QProcess::ForwardedChannels);
           m_qmlPuppetPreviewProcess->start(applicationPath, QStringList() << socketToken << "previewmode" << "-graphicssystem raster");
158

159
           m_qmlPuppetRenderProcess = new QProcess;
160 161
           m_qmlPuppetRenderProcess->setProcessEnvironment(enviroment);
           m_qmlPuppetRenderProcess->setObjectName("RenderProcess");
162 163 164 165 166
           connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
           connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), m_qmlPuppetRenderProcess.data(), SLOT(kill()));
           if (fowardQmlpuppetOutput)
               m_qmlPuppetRenderProcess->setProcessChannelMode(QProcess::ForwardedChannels);
           m_qmlPuppetRenderProcess->start(applicationPath, QStringList() << socketToken << "rendermode" << "-graphicssystem raster");
167

168
       }
169

170
       connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
171

172 173
       if (m_qmlPuppetEditorProcess->waitForStarted(10000)) {
           connect(m_qmlPuppetEditorProcess.data(), SIGNAL(finished(int)), m_qmlPuppetEditorProcess.data(),SLOT(deleteLater()));
174

175 176 177
           if (runModus == NormalModus) {
               m_qmlPuppetPreviewProcess->waitForStarted();
               connect(m_qmlPuppetPreviewProcess.data(), SIGNAL(finished(int)), m_qmlPuppetPreviewProcess.data(),SLOT(deleteLater()));
178

179 180 181
               m_qmlPuppetRenderProcess->waitForStarted();
               connect(m_qmlPuppetRenderProcess.data(), SIGNAL(finished(int)), m_qmlPuppetRenderProcess.data(),SLOT(deleteLater()));
           }
182

183 184
           if (!m_localServer->hasPendingConnections())
               m_localServer->waitForNewConnection(10000);
185

186 187
           m_firstSocket = m_localServer->nextPendingConnection();
           connect(m_firstSocket.data(), SIGNAL(readyRead()), this, SLOT(readFirstDataStream()));
188

189 190 191
           if (runModus == NormalModus) {
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
192

193 194
               m_secondSocket = m_localServer->nextPendingConnection();
               connect(m_secondSocket.data(), SIGNAL(readyRead()), this, SLOT(readSecondDataStream()));
195

196 197
               if (!m_localServer->hasPendingConnections())
                   m_localServer->waitForNewConnection(10000);
198

199 200 201
               m_thirdSocket = m_localServer->nextPendingConnection();
               connect(m_thirdSocket.data(), SIGNAL(readyRead()), this, SLOT(readThirdDataStream()));
           }
202

203 204 205 206 207 208
       } else {
           QMessageBox::warning(0, tr("Cannot Start QML Puppet Executable"),
                                tr("The executable of the QML Puppet process (%1) cannot be started. "
                                   "Please check your installation. "
                                   "QML Puppet is a process which runs in the background to render the items.").
                                arg(applicationPath));
209
       }
210

211 212 213
       m_localServer->close();

   } else {
214 215 216
       QMessageBox::warning(0, tr("Cannot Find QML Puppet Executable"),
                            tr("The executable of the QML Puppet process (%1) cannot be found. "
                               "Please check your installation. "
Jarek Kobus's avatar
Jarek Kobus committed
217
                               "QML Puppet is a process which runs in the background to render the items.").
218
                            arg(applicationPath));
219
   }
220 221 222 223
}

NodeInstanceServerProxy::~NodeInstanceServerProxy()
{
224 225
    disconnect(this, SLOT(processFinished(int,QProcess::ExitStatus)));

226 227 228 229 230 231
    if (m_firstSocket)
        m_firstSocket->close();

    if (m_secondSocket)
        m_secondSocket->close();

232 233 234
    if(m_thirdSocket)
        m_thirdSocket->close();

235

236
    if (m_qmlPuppetEditorProcess)
237
        m_qmlPuppetEditorProcess->terminate();
238

239
    if (m_qmlPuppetPreviewProcess)
240
        m_qmlPuppetPreviewProcess->terminate();
241 242

    if (m_qmlPuppetRenderProcess)
243
        m_qmlPuppetRenderProcess->terminate();
244 245 246 247 248 249 250
}

void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
{
    static const int informationChangedCommandType = QMetaType::type("InformationChangedCommand");
    static const int valuesChangedCommandType = QMetaType::type("ValuesChangedCommand");
    static const int pixmapChangedCommandType = QMetaType::type("PixmapChangedCommand");
251
    static const int childrenChangedCommandType = QMetaType::type("ChildrenChangedCommand");
252
    static const int statePreviewImageChangedCommandType = QMetaType::type("StatePreviewImageChangedCommand");
253
    static const int componentCompletedCommandType = QMetaType::type("ComponentCompletedCommand");
254
    static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand");
255
    static const int tokenCommandType = QMetaType::type("TokenCommand");
256 257 258 259 260 261 262

    if (command.userType() ==  informationChangedCommandType)
        nodeInstanceClient()->informationChanged(command.value<InformationChangedCommand>());
    else if (command.userType() ==  valuesChangedCommandType)
        nodeInstanceClient()->valuesChanged(command.value<ValuesChangedCommand>());
    else if (command.userType() ==  pixmapChangedCommandType)
        nodeInstanceClient()->pixmapChanged(command.value<PixmapChangedCommand>());
263 264
    else if (command.userType() == childrenChangedCommandType)
        nodeInstanceClient()->childrenChanged(command.value<ChildrenChangedCommand>());
265 266
    else if (command.userType() == statePreviewImageChangedCommandType)
        nodeInstanceClient()->statePreviewImagesChanged(command.value<StatePreviewImageChangedCommand>());
267 268
    else if (command.userType() == componentCompletedCommandType)
        nodeInstanceClient()->componentCompleted(command.value<ComponentCompletedCommand>());
269 270
    else if (command.userType() == tokenCommandType)
        nodeInstanceClient()->token(command.value<TokenCommand>());
271 272 273 274
    else if (command.userType() == synchronizeCommandType) {
        SynchronizeCommand synchronizeCommand = command.value<SynchronizeCommand>();
        m_synchronizeId = synchronizeCommand.synchronizeId();
    }  else
275 276 277 278 279 280 281 282
        Q_ASSERT(false);
}

NodeInstanceClientInterface *NodeInstanceServerProxy::nodeInstanceClient() const
{
    return m_nodeInstanceView.data();
}

283
static void writeCommandToSocket(const QVariant &command, QLocalSocket *socket, unsigned int commandCounter)
284
{
285 286 287 288
    if(socket) {
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out << quint32(0);
289
        out << quint32(commandCounter);
290 291 292 293 294 295
        out << command;
        out.device()->seek(0);
        out << quint32(block.size() - sizeof(quint32));

        socket->write(block);
    }
296 297 298 299
}

void NodeInstanceServerProxy::writeCommand(const QVariant &command)
{
300 301 302 303
    writeCommandToSocket(command, m_firstSocket.data(), m_writeCommandCounter);
    writeCommandToSocket(command, m_secondSocket.data(), m_writeCommandCounter);
    writeCommandToSocket(command, m_thirdSocket.data(), m_writeCommandCounter);
    m_writeCommandCounter++;
304 305 306 307 308
    if (m_runModus == TestModus) {
        static int synchronizeId = 0;
        synchronizeId++;
        SynchronizeCommand synchronizeCommand(synchronizeId);

309 310
        writeCommandToSocket(QVariant::fromValue(synchronizeCommand), m_firstSocket.data(), m_writeCommandCounter);
        m_writeCommandCounter++;
311 312 313 314 315 316 317

        while(m_firstSocket->waitForReadyRead()) {
                readFirstDataStream();
                if (m_synchronizeId == synchronizeId)
                    return;
        }
    }
318 319
}

320
void NodeInstanceServerProxy::processFinished(int /*exitCode*/, QProcess::ExitStatus exitStatus)
321
{
322
    qDebug() << "Process finished:" << sender();
323 324 325 326
    if (m_firstSocket)
        m_firstSocket->close();
    if (m_secondSocket)
        m_secondSocket->close();
327 328 329
    if (m_thirdSocket)
        m_thirdSocket->close();

330 331
    if (exitStatus == QProcess::CrashExit)
        emit processCrashed();
332 333
}

334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
void NodeInstanceServerProxy::readFirstDataStream()
{
    QList<QVariant> commandList;

    while (!m_firstSocket->atEnd()) {
        if (m_firstSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_firstSocket.data());

        if (m_firstBlockSize == 0) {
            in >> m_firstBlockSize;
        }

        if (m_firstSocket->bytesAvailable() < m_firstBlockSize)
            break;

352 353 354 355 356 357 358 359
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_firstLastReadCommandCounter == 0 && commandCounter == 0) || (m_firstLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_firstLastReadCommandCounter <<  commandCounter;
        m_firstLastReadCommandCounter = commandCounter;


360 361 362 363 364 365 366 367 368 369 370 371 372
        QVariant command;
        in >> command;
        m_firstBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

void NodeInstanceServerProxy::readSecondDataStream()
373 374 375
{
    QList<QVariant> commandList;

376 377
    while (!m_secondSocket->atEnd()) {
        if (m_secondSocket->bytesAvailable() < int(sizeof(quint32)))
378 379
            break;

380
        QDataStream in(m_secondSocket.data());
381

382 383
        if (m_secondBlockSize == 0) {
            in >> m_secondBlockSize;
384 385
        }

386
        if (m_secondSocket->bytesAvailable() < m_secondBlockSize)
387 388
            break;

389 390 391 392 393 394 395 396
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_secondLastReadCommandCounter == 0 && commandCounter == 0) || (m_secondLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_secondLastReadCommandCounter <<  commandCounter;
        m_secondLastReadCommandCounter = commandCounter;


397 398
        QVariant command;
        in >> command;
399
        m_secondBlockSize = 0;
400 401 402 403 404 405 406 407 408

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
void NodeInstanceServerProxy::readThirdDataStream()
{
    QList<QVariant> commandList;

    while (!m_thirdSocket->atEnd()) {
        if (m_thirdSocket->bytesAvailable() < int(sizeof(quint32)))
            break;

        QDataStream in(m_thirdSocket.data());

        if (m_thirdBlockSize == 0) {
            in >> m_thirdBlockSize;
        }

        if (m_thirdSocket->bytesAvailable() < m_thirdBlockSize)
            break;

426 427 428 429 430 431 432 433
        quint32 commandCounter;
        in >> commandCounter;
        bool commandLost = !((m_thirdLastReadCommandCounter == 0 && commandCounter == 0) || (m_thirdLastReadCommandCounter + 1 == commandCounter));
        if (commandLost)
            qDebug() << "server command lost: " << m_thirdLastReadCommandCounter <<  commandCounter;
        m_thirdLastReadCommandCounter = commandCounter;


434 435 436 437 438 439 440 441 442 443 444 445
        QVariant command;
        in >> command;
        m_thirdBlockSize = 0;

        commandList.append(command);
    }

    foreach (const QVariant &command, commandList) {
        dispatchCommand(command);
    }
}

446 447
QString NodeInstanceServerProxy::qmlPuppetApplicationName() const
{
448 449 450
    if (hasQtQuick2(m_nodeInstanceView.data()))
        return QLatin1String("qml2puppet" QTC_HOST_EXE_SUFFIX);
    return QLatin1String("qmlpuppet" QTC_HOST_EXE_SUFFIX);
451 452 453 454 455
}

QString NodeInstanceServerProxy::macOSBundlePath(const QString &path) const
{
    QString applicationPath = path;
456 457
    if (Utils::HostOsInfo::isMacHost())
        applicationPath += QLatin1String("/qmlpuppet.app/Contents/MacOS");
458 459
   return applicationPath;
}
460

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
void NodeInstanceServerProxy::createInstances(const CreateInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeFileUrl(const ChangeFileUrlCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::createScene(const CreateSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::clearScene(const ClearSceneCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeInstances(const RemoveInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::removeProperties(const RemovePropertiesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyBindings(const ChangeBindingsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changePropertyValues(const ChangeValuesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

501 502 503 504 505
void NodeInstanceServerProxy::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
void NodeInstanceServerProxy::reparentInstances(const ReparentInstancesCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeIds(const ChangeIdsCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

void NodeInstanceServerProxy::changeState(const ChangeStateCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

521 522 523 524
void NodeInstanceServerProxy::completeComponent(const CompleteComponentCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}
525

526
void NodeInstanceServerProxy::changeNodeSource(const ChangeNodeSourceCommand &command)
527 528 529 530
{
    writeCommand(QVariant::fromValue(command));
}

531 532 533 534 535
void NodeInstanceServerProxy::token(const TokenCommand &command)
{
    writeCommand(QVariant::fromValue(command));
}

536 537 538 539 540
void NodeInstanceServerProxy::removeSharedMemory(const RemoveSharedMemoryCommand &command)
{
   writeCommand(QVariant::fromValue(command));
}

541
} // namespace QmlDesigner